Directory cleanup

This commit is contained in:
calzoneman 2014-02-18 22:00:56 -06:00
parent c54915e940
commit 644437ea42
54 changed files with 16 additions and 24502 deletions

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.
*/
const VERSION = "3.0.0-RC1";
const VERSION = "3.0.0-RC2";
var singleton = null;
var Config = require("./config");

View File

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

View File

@ -130,7 +130,7 @@ html(lang="en")
var USEROPTS = { secure_connection: true };
script(src=sioSource)
script(src="/sioconfig")
script(src="/assets/js/util.js")
script(src="/assets/js/paginator.js")
script(src="/assets/js/chart.js")
script(src="/js/util.js")
script(src="/js/paginator.js")
script(src="/js/chart.js")
script(src="/js/acp.js")

View File

@ -212,16 +212,16 @@ html(lang="en")
include footer
mixin footer()
script(src=sioSource)
script(src="/assets/js/data.js")
script(src="/js/data.js")
script(src="/sioconfig")
script(src="/assets/js/util.js")
script(src="/assets/js/player.js")
script(src="/assets/js/paginator.js")
script(src="/assets/js/ui.js")
script(src="/assets/js/callbacks.js")
script(src="/js/util.js")
script(src="/js/player.js")
script(src="/js/paginator.js")
script(src="/js/ui.js")
script(src="/js/callbacks.js")
script(defer, src="https://www.youtube.com/iframe_api")
script(defer, src="//api.dmcdn.net/all.js")
script(defer, src="/assets/js/jwplayer.js")
script(defer, src="/assets/js/sc.js")
script(defer, src="/assets/js/froogaloop.min.js")
script(defer, src="/assets/js/swf.js")
script(defer, src="/js/jwplayer.js")
script(defer, src="/js/sc.js")
script(defer, src="/js/froogaloop.min.js")
script(defer, src="/js/swf.js")

View File

@ -1,34 +0,0 @@
var io = require('socket.io-client');
function testLogin() {
var socket = io.connect('http://localhost:1337');
socket.on('connect', function () {
socket.emit('login', { name: 'test', pw: 'test' });
socket.emit('joinChannel', { name: 'test' });
socket.disconnect();
});
}
function testBan() {
var socket = io.connect('http://localhost:1337');
socket.on('connect', function () {
socket.emit('login', { name: 'test', pw: 'test' });
socket.emit('joinChannel', { name: 'test' });
socket.emit('chatMsg', { msg: '/ban asdf' });
socket.disconnect();
});
}
function testRankChange() {
var socket = io.connect('http://localhost:1337');
socket.on('connect', function () {
socket.emit('login', { name: 'test', pw: 'test' });
socket.emit('joinChannel', { name: 'test' });
socket.emit('setChannelRank', { user: 'test2', rank: 2 });
socket.disconnect();
});
}
testLogin();
testBan();
testRankChange();

View File

@ -1,9 +0,0 @@
var io = require('socket.io-client');
var socket = io.connect('http://localhost:1337');
// connect, join a room, then disconnect as quickly as possible
socket.on('connect', function () {
socket.emit('joinChannel', { name: 'test' });
socket.disconnect();
});

View File

@ -1,48 +0,0 @@
var fs = require('fs');
var io = require('socket.io-client');
var socket = io.connect('http://localhost:1337');
socket.on('connect', function () {
socket.emit('login', { name: 'test', pw: 'test' });
socket.emit('joinChannel', { name: 'test' });
});
socket.on('login', testAddVideos);
socket.on('queueFail', function (msg) {
console.log(msg);
});
/* Stress test adding a lot of videos in a very short timespan */
function testAddVideos() {
var pl = fs.readFileSync('largepl.json') + '';
pl = JSON.parse(pl);
var ids = [];
for (var i = 0; i < pl.length; i++) {
if (pl[i].type === 'yt')
ids.push(pl[i].id);
}
// burst the first 10
for (var i = 0; i < 10; i++) {
console.log('queue', ids[i]);
socket.emit('queue', {
id: ids[i],
type: 'yt',
pos: 'end'
});
}
for (var i = 10; i < ids.length; i++) {
(function (i) {
setTimeout(function () {
console.log('queue', ids[i]);
socket.emit('queue', {
id: ids[i],
type: 'yt',
pos: 'end'
});
}, 1050 * (i - 9));
})(i);
}
}

View File

@ -1,52 +0,0 @@
var $util = require('../lib/utilities.js');
function testBurst() {
var lim = $util.newRateLimiter();
var params = {
burst: 10,
sustained: 2
};
for (var i = 0; i < 10; i++) {
if (lim.throttle(params)) {
console.log("[FAIL] Burst: Unexpected throttle");
return;
}
}
if (!lim.throttle(params)) {
console.log("[FAIL] Burst: didn't throttle after exceeding burst amount");
return;
}
console.log("[PASS] Burst");
}
function testBurstAndWait() {
var lim = $util.newRateLimiter();
var params = {
burst: 10,
sustained: 2
};
for (var i = 0; i < 9; i++) {
if (lim.throttle(params)) {
console.log("[FAIL] Burst & Wait: Unexpected throttle");
return;
}
}
// Wait a while and try some more
setTimeout(function () {
for (var i = 9; i < 17; i++) {
if (lim.throttle(params)) {
console.log("[FAIL] Burst & Wait: Unexpected throttle");
return;
}
}
console.log("[PASS] Burst & Wait");
}, 6000);
}
testBurst();
testBurstAndWait();

View File

@ -1,46 +0,0 @@
var sanitize = require('../lib/xss');
var sanitizeHTML = sanitize.sanitizeHTML;
var sanitizeText = sanitize.sanitizeText;
var decodeText = sanitize.decodeText;
var assert = require('assert');
var failed = 0;
function doTest(s, src, expected) {
try {
assert(s(src) === expected);
} catch (e) {
failed++;
console.log("Expected '" + expected + "'");
console.log("Got '" + s(src) + "'");
}
}
function testSanitizeHTML() {
doTest(sanitizeHTML, "< script src = bad.js>blah</script>", "[tag removed]blah[tag removed]");
doTest(sanitizeHTML, "< img src=asdf onerror='alert(\"xss\")'>", "<img src=\"asdf\">");
doTest(sanitizeHTML, "<a href='javascript:alert(document.cookie)'>", "<a href=\"[removed]:[removed]([removed])\">");
doTest(sanitizeHTML, "<a ", "<a>");
doTest(sanitizeHTML, "<img src=\"<a href=\"javascript:void(0)\">>", "<img src=\"<a href=\" javascriptvoid0>>");
}
function testSanitizeText() {
doTest(sanitizeText, "<a href=\"#\" onerror=\"javascript:alert('xss')\">", "&lt;a href=&quot;#&quot; onerror=&quot;javascript:alert&#40;&#39;xss&#39;&#41;&quot;&gt;");
doTest(sanitizeText, "&lt;&gt;&amp;&quot;&ccedil;&#x09", "&amp;lt;&amp;gt;&amp;amp;&amp;quot;&amp;ccedil;&amp;#x09");
}
function testDecode() {
doTest(decodeText, "&lt;a href=&quot;#&quot; onerror=&quot;javascript:alert&#40;&#39;xss&#39;&#41;&quot;&gt;", "<a href=\"#\" onerror=\"javascript:alert('xss')\">");
doTest(decodeText, "&amp;lt;&amp;gt;&amp;amp;&amp;quot;&amp;ccedil;&amp;#x09", "&lt;&gt;&amp;&quot;&ccedil;&#x09");
}
testSanitizeHTML();
testSanitizeText();
testDecode();
if (!failed)
console.log("Tests passed.");
else
console.log(""+failed, "tests failed");

View File

@ -1,247 +0,0 @@
<!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="">
<meta name="author" content="Calvin 'calzoneman' Montgomery">
<link href="./assets/css/bootstrap.css" rel="stylesheet">
<link href="./assets/css/ytsync.css" rel="stylesheet">
<style>
body {
padding-top: 60px; /* 60px to make the container go all the way to the bottom of the topbar */
}
</style>
<link href="./assets/css/bootstrap-responsive.css" rel="stylesheet">
</head>
<body>
<div class="wrapper">
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a class="brand" href="index.html">CyTube</a>
<div class="">
<ul class="nav">
<li><a href="index.html">Home</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>
</ul>
</div>
</div>
</div>
</div>
<div class="container">
<div class="row">
<div class="span4 well" style="padding: 8px 0;">
<ul class="nav nav-list" id="accountnav">
<li class="nav-header">Not Logged In</li>
<li><a href="javascript:void(0)" id="register">Register</a></li>
<li><a href="javascript:void(0)" id="login">Login</a></li>
<li class="nav-header">Account Control</li>
<li><a href="javascript:void(0)" id="pwchange">Change Password</a></li>
<li><a href="javascript:void(0)" id="pwreset">Reset Password</a></li>
<li><a href="javascript:void(0)" id="profile">Edit Profile</a></li>
<li><a href="javascript:void(0)" id="email">Change Email</a></li>
<li><a href="javascript:void(0)" id="channels">My Channels</a></li>
</ul>
</div>
<div class="span7">
<h3>Select an action from the sidebar</h3>
</div>
<div class="span7" id="registerpane" style="display: none">
<h3>Register New Account</h3>
<form class="form-horizontal" action="javascript:void(0)">
<div class="control-group">
<label class="control-label" for="regusername">Username</label>
<div class="controls">
<input type="text" id="regusername">
</div>
</div>
<div class="control-group">
<label class="control-label" for="regpw">Password</label>
<div class="controls">
<input type="password" id="regpw">
</div>
</div>
<div class="control-group">
<label class="control-label" for="regpwconfirm">Confirm Password</label>
<div class="controls">
<input type="password" id="regpwconfirm">
</div>
</div>
<div class="control-group">
<div class="controls">
<button class="btn btn-primary" id="registerbtn">Register</button>
</div>
</div>
</form>
</div>
<div class="span7" id="changepwpane" style="display: none">
<h3>Change Password</h3>
<form class="form-horizontal" action="javascript:void(0)">
<div class="control-group">
<label class="control-label" for="cpwusername">Username</label>
<div class="controls">
<input type="text" id="cpwusername">
</div>
</div>
<div class="control-group">
<label class="control-label" for="cpwoldpw">Old Password</label>
<div class="controls">
<input type="password" id="cpwoldpw">
</div>
</div>
<div class="control-group">
<label class="control-label" for="cpwnewpw">New Password</label>
<div class="controls">
<input type="password" id="cpwnewpw">
</div>
</div>
<div class="control-group">
<label class="control-label" for="cpwconfirm">Confirm New Password</label>
<div class="controls">
<input type="password" id="cpwconfirm">
</div>
</div>
<div class="control-group">
<div class="controls">
<button class="btn btn-primary" id="cpwbtn">Change Password</button>
</div>
</div>
</form>
</div>
<div class="span7" id="loginpane" style="display: none">
<h3>Login</h3>
<form class="form-horizontal" action="javascript:void(0)">
<div class="control-group">
<label class="control-label" for="loginusername">Username</label>
<div class="controls">
<input type="text" id="loginusername">
</div>
</div>
<div class="control-group">
<label class="control-label" for="loginpw">Password</label>
<div class="controls">
<input type="password" id="loginpw">
</div>
</div>
<div class="control-group">
<div class="controls">
<button class="btn btn-primary" id="loginbtn">Login</button>
</div>
</div>
</form>
</div>
<div class="span7" id="changeemailpane" style="display: none">
<h3>Change Email</h3>
<form class="form-horizontal" action="javascript:void(0)">
<div class="control-group">
<label class="control-label" for="ceusername">Username</label>
<div class="controls">
<input type="text" id="ceusername">
</div>
</div>
<div class="control-group">
<label class="control-label" for="cepw">Password</label>
<div class="controls">
<input type="password" id="cepw">
</div>
</div>
<div class="control-group">
<label class="control-label" for="ceemail">Email</label>
<div class="controls">
<input type="text" id="ceemail">
</div>
</div>
<div class="control-group">
<div class="controls">
<button class="btn btn-primary" id="cebtn">Update</button>
</div>
</div>
</form>
</div>
<div class="span7" id="pwresetpane" style="display: none">
<h3>Reset Password</h3>
<form class="form-horizontal" action="javascript:void(0)">
<div class="control-group">
<label class="control-label" for="rpusername">Username</label>
<div class="controls">
<input type="text" id="rpusername">
</div>
</div>
<div class="control-group">
<label class="control-label" for="rpemail">Email</label>
<div class="controls">
<input type="text" id="rpemail">
</div>
</div>
<div class="control-group">
<div class="controls">
<button class="btn btn-primary" id="rpbtn">Send Reset</button>
</div>
</div>
</form>
</div>
<div class="span7" id="profilepane" style="display: none">
<h3>Profile</h3>
<form class="form-horizontal" action="javascript:void(0)">
<div class="control-group">
<label class="control-label" for="profileimg">Image</label>
<div class="controls">
<input type="text" id="profileimg">
</div>
</div>
<div class="control-group">
<label class="control-label" for="profiletext">Text</label>
<div class="controls">
<textarea rows="10" id="profiletext"></textarea>
</div>
</div>
<div class="control-group">
<div class="controls">
<button class="btn btn-primary" id="profilesave">Save</button>
</div>
</div>
</form>
</div>
<div class="span7" id="channelspane" style="display: none">
<h1>My Channels</h1>
<table id="channellist" class="table table-bordered">
<thead>
<tr>
<th>Name</th>
</tr>
</thead>
</table>
</div>
</div>
</div> <!-- /container -->
<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>
<!-- Third party -->
<script src="./assets/js/jquery.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>
<!-- Mine -->
<script src="./assets/js/data.js"></script>
<script src="./assets/js/iourl.js"></script>
<script src="./assets/js/account.js"></script>
</body>
</html>

View File

@ -1,282 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>CyTube - Administration</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="Calvin 'calzoneman' Montgomery">
<link href="./assets/css/bootstrap.css" rel="stylesheet">
<link href="./assets/css/ytsync.css" rel="stylesheet">
<style>
body {
padding-top: 60px; /* 60px to make the container go all the way to the bottom of the topbar */
}
#log {
max-height: 500px;
overflow-y: scroll;
}
</style>
<link href="./assets/css/bootstrap-responsive.css" rel="stylesheet">
</head>
<body>
<div class="wrapper">
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a class="brand" href="index.html">CyTube</a>
<div class="">
<ul class="nav">
<li class="active"><a href="index.html">Home</a></li>
<li class="dropdown">
<a class="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_chanlookup"><a href="javascript:void(0)" id="show_chanlookup">Channels</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>
<li id="li_stats"><a href="javascript:void(0)" id="show_stats">Server Stats</a></li>
</ul>
</li>
</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 class="container">
<div class="row">
<div class="span12">
<div class="row">
<div class="span12">
</div>
</div>
<div class="row" id="panels">
<div class="span12" id="logview">
<h3>Log Viewer</h3>
<form class="form-inline" action="javascript:void(0);">
<button id="syslog" class="btn">Syslog</button>
<button id="errlog" class="btn">Error log</button>
<div class="input-append">
<input type="text" id="channame" placeholder="Channel Name">
<button class="btn" id="chanlog">Channel log</button>
</div>
<button class="btn" id="log_reverse">Reverse Log</button>
</form>
<pre id="log"></pre>
</div>
<div class="span12" id="announcepanel">
<h3 id="announce_current_h3">Current Announcement</h3>
<h3>New Announcement</h3>
<form class="form-horizontal" action="javascript:void(0)">
<div class="control-group">
<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>
</form>
</div>
<div class="span12" id="gbanpanel">
<h3>Global Bans</h3>
<table class="table table-striped table-bordered">
<thead>
<tr>
<th style="width: 80px;">Remove</th>
<th style="width: 160px;">IP Address</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="chanlookup">
<h3>Channels</h3>
<form class="form-inline" action="javascript:void(0)">
<input type="text" id="chanlookup_value" placeholder="Name">
<select id="chanlookup_field">
<option value="name">Channel Name</option>
<option value="owner">Channel Owner</option>
</select>
<button class="btn" id="chanlookup_submit">Search</button>
</form>
<table class="table table-striped table-bordered">
<thead>
<tr>
<th id="chanlookup_id">ID</th>
<th id="chanlookup_name">Name</th>
<th id="chanlookup_owner">Owner</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>
<br>
<form class="form-inline" action="javascript:void(0)">
<input type="text" placeholder="Search" id="actionlog_search">
<label for="actionlog_sfield">Search For</label>
<select id="actionlog_sfield">
<option value="ip">IP Address</option>
<option value="name" selected="selected">Name</option>
<option value="time">Time</option>
</select>
<label for="actionlog_sort">Sort By</label>
<select id="actionlog_sort">
<option value="ip">IP Address</option>
<option value="name">Name</option>
<option value="action">Action</option>
<option value="time" selected="selected">Time</option>
</select>
<select id="actionlog_sortorder">
<option value="true">Descending</option>
<option value="false">Ascending</option>
</select>
<button class="btn" id="actionlog_searchbtn">Go</button>
</form>
<table class="table table-bordered table-striped table-compact">
<thead>
<tr>
<th id="actionlog_rem">Remove</th>
<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>
</thead>
</table>
</div>
<div class="span12" id="stats">
<h3>User Count</h3>
<canvas id="stat_users" width="1170" height="400"></canvas>
<h3>Channel Count</h3>
<canvas id="stat_channels" width="1170" height="400"></canvas>
<h3>Memory Usage (MB)</h3>
<canvas id="stat_mem" width="1170" height="400"></canvas>
</div>
</div>
</div>
</div>
</div> <!-- /container -->
<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>
<!-- Third party -->
<script src="./assets/js/jquery.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>
<script src="./assets/js/chart.js"></script>
<!-- Mine -->
<script src="./assets/js/data.js"></script>
<script src="./assets/js/iourl.js"></script>
<script src="./assets/js/paginator.js"></script>
<script src="./assets/js/util.js"></script>
<script src="./assets/js/acp.js"></script>
</body>
</html>

View File

@ -1,223 +0,0 @@
body {
background-color: #000;
color: #ccc;
}
.well {
background-color: #222!important;
border: 1px solid #555;
}
#usercountwrap, #userlist, #messagebuffer, #videowrap {
background-color: #111;
}
#usercountwrap, #currenttitle {
border: 1px solid #555;
border-bottom: none;
margin: 0;
}
#userlist, #messagebuffer {
border-color: #555;
}
input, textarea {
border-color: #555!important;
background: #222!important;
}
textarea, input {
color: #ccc!important;
}
button, .btn, .dropdown-menu, select, option {
background: #111!important;
border-color: #555!important;
color: #aaa!important;
text-shadow: none!important;
box-shadow: none!important;
}
.dropdown-menu > li > a {
color: #ccc!important;
}
.btn:hover {
background-color: #333!important;
}
.btn-success {
background: #009900!important;
border: 1px solid #006600!important;
}
.btn-success:hover {
background: #00bb00!important;
}
.btn-danger {
background: #990000!important;
border: 1px solid #660000!important;
}
.btn-danger:hover {
background: #bb0000!important;
}
.btn-primary {
background: #000099!important;
border: 1px solid #000066!important;
}
.btn-primary:hover {
background: #0000bb!important;
}
input, textarea, button, .well, .btn, .dropdown-menu, select {
border-radius: 0!important;
}
.videolist {
background-color: #000;
}
.queue_entry {
background-color: #000!important;
border: 1px solid #ccc;
color: #ccc;
}
.queue_entry > a {
color: #ccc;
}
.queue_active {
background-color: #555!important;
}
#plmeta {
border-color: #555;
background-color: #222;
}
[class^="icon-"],
[class*=" icon-"] {
background-image: url("../img/glyphicons-halflings-white.png");
}
.profile-box, .user-dropdown {
color: #ccc!important;
background-color: #222;
border: 1px solid #ccc!important;
border-radius: 0;
}
.close {
background: none!important;
}
.alert {
border-radius: 0;
text-shadow: none!important;
}
hr {
border: 1px solid #111!important;
}
.alert-error {
background-color: #550000;
border: 1px solid #880000;
color: #ff9999;
}
.alert-info {
background-color: #000055;
border: 1px solid #000088;
color: #9999ff;
}
.alert-warning {
background-color: #555500;
border: 1px solid #888800;
color: #ffff99;
}
.table-striped tbody > tr:nth-child(odd) > td {
background-color: #111!important;
}
.table-striped tbody > tr:nth-child(even) > td {
background-color: #000!important;
}
.table td, .table tr, .table th {
border-color: #ccc!important;
}
.table-bordered, .table-bordered tr, .table-bordered td,
.table-bordered th {
border-radius: 0!important;
border-top-left-radius: 0!important;
border-bottom-left-radius: 0!important;
border-top-right-radius: 0!important;
border-bottom-right-radius: 0!important;
}
.pagination ul > li:first-child > a,
.pagination ul > li:last-child > a {
border-radius: 0!important;
}
.pagination ul > li > a, .pagination > ul > li > span {
background-color: #222!important;
}
pre {
border: 1px solid #ccc!important;
background-color: #222!important;
color: #ccc!important;
border-radius: 0!important;
}
.userlist_owner {
color: #ff9900;
}
.nick-hover {
background-color: #444;
}
.nick-highlight {
background-color: #555;
}
.drink {
border-color: #ff9900;
}
.modal {
background-color: #222!important;
}
.modal-header, .modal-footer {
background-color: #222!important;
}
.caret {
border-top: 4px solid #ccc!important;
}
.dropup .caret {
border-top: none!important;
border-bottom: 4px solid #ccc!important;
}
#footer {
background-color: #222;
color: #ccc;
}
.poll-notify {
color: #ff6600;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -1,129 +0,0 @@
body {
background-color: #333333;
}
.well, .dropdown, .dropdown-menu,
.dropdown-menu li, .dropdown-menu li a {
background-color: #222222;
color: #ffffff;
}
input[type="text"] {
background-color: #000000;
color: #ffffff;
}
legend {
color: #ffffff;
}
.dropdown-menu {
border: 1px solid #ffffff;
}
.close {
color: #ffffff;
}
.alert {
background-color: #000066;
color: #ffffff;
}
.alert-error {
background-color: #660000;
color: #ffffff;
}
.videolist li {
color: #ffffff;
background-color: #000000;
border: 1px solid #ffffff;
}
.videolist li.alert-info {
background-color: #000055;
border: 1px solid #0000ff;
}
.qe_btn {
height: 20px;
font-family: Monospace;
padding: 0 5px;
margin: auto;
overflow: hidden;
}
.qe_buttons {
float: left;
}
.qe_title {
float: left;
}
.qe_time {
float: right;
}
.qe_clear {
clear: both;
}
#library {
width: 640px;
}
#userlist {
background-color: #000000;
color: #ffffff
}
#messagebuffer {
background-color: #000000;
color: #ffffff
}
#chatline {
background-color: #000000;
color: #ffffff;
}
.userlist_siteadmin {
color: #cc0000;
font-weight: bold;
}
.userlist_owner {
color: #0000cc;
font-weight: bold;
}
.userlist_op {
color: #00aa00;
}
.action {
color: #dddddd;
}
.spoiler {
color: #ffffff;
background-color: #ffffff;
}
.spoiler:hover {
color: #000000;
}
.greentext {
color: #789922; /* Color value directly from 4chan */
}
.poll-notify {
color: #5555ff;
}
.nick-highlight {
background-color: #446666;
}

File diff suppressed because it is too large Load Diff

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 206 B

View File

@ -1,412 +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.
*/
/*
So, it turns out that $.post causes Firefox to use a GET request
on cross-site requests. What the hell? I'd understand if they just
made it error instead, but why give me chicken tenders if I ordered a
cheeseburger and act like everything's peachy?
*/
function postJSON(url, data, callback) {
$.ajax(url, {
method: "POST",
crossDomain: true,
data: data,
success: function (data) {
try {
data = data.substring(data.indexOf("{"));
data = data.substring(0, data.lastIndexOf("}") + 1);
data = JSON.parse(data);
callback(data);
} catch(e) {
return;
}
},
dataType: "text"
});
}
var uname = readCookie("cytube_uname") || "";
var session = readCookie("cytube_session") || "";
var loggedin = false;
if(uname && session) {
var data = {
name: uname,
session: session
};
postJSON(WEB_URL + "/api/login?callback=?", data, function (data) {
console.log(data);
if(data.success)
onLogin();
});
}
function onLogin() {
$("#cpwusername").val(uname);
$("#ceusername").val(uname);
$("#accountnav li")[0].innerHTML = "Logged in as " + uname;
$("#register").hide();
loggedin = true;
$("#login").text("Logout");
createCookie("cytube_uname", uname, 7);
createCookie("cytube_session", session, 7);
}
function makeTabCallback(tabid, paneid) {
return function() {
$("#accountnav li").each(function() {
$(this).removeClass("active");
});
$(tabid).parent().addClass("active");
$(".span7").each(function() {
$(this).css("display", "none");
});
$(paneid).css("display", "");
};
}
$("#register").click(makeTabCallback("#register", "#registerpane"));
$("#pwchange").click(makeTabCallback("#pwchange", "#changepwpane"));
$("#pwreset").click(makeTabCallback("#pwreset", "#pwresetpane"));
$("#email").click(makeTabCallback("#email", "#changeemailpane"));
$("#profile").click(makeTabCallback("#profile", "#profilepane"));
$("#profile").click(function() {
if(uname != "") {
$.getJSON(WEB_URL+"/api/users/"+uname+"/profile?callback=?",
function (data) {
if(data.success) {
$("#profiletext").val(data.profile_text);
$("#profileimg").val(data.profile_image);
}
else {
$("<div/>").addClass("alert alert-error")
.text("Failed to retrieve profile: " + data.error)
.insertBefore($("#profilepane form"));
}
});
}
});
$("#channels").click(makeTabCallback("#channels", "#channelspane"));
$("#channels").click(function () {
if(!loggedin) {
var error = $("<div/>").addClass("alert alert-error")
.text("You must be logged in to view this page")
.insertBefore($("#channellist"));
$("<button/>").addClass("close pull-right").click(function () {
error.remove();
}).html("&times;").prependTo(error);
return;
}
var auth = "name=" + encodeURIComponent(uname) + "&session=" +
encodeURIComponent(session);
$.getJSON(WEB_URL+"/api/account/mychannels?"+auth+"&callback=?",
function (data) {
$("#channellist tbody").remove();
data.channels.forEach(function (chan) {
var tr = $("<tr/>").appendTo($("#channellist"));
var td = $("<td/>").appendTo(tr);
$("<a/>").attr("href", "./r/" + chan.name)
.attr("target", "_blank")
.text(chan.name)
.appendTo(td);
});
});
});
$("#registerbtn").click(function() {
$("#registerpane").find(".alert-error").remove();
$("#registerpane").find(".error").removeClass("error");
var name = $("#regusername").val();
var pw = $("#regpw").val();
var pwc = $("#regpwconfirm").val();
var err = false;
if(!name.match(/^[-\w\u00c0-\u00ff]{1,20}$/i)) {
$("<div/>").addClass("alert alert-error")
.text("Usernames must be 1-20 characters long and contain only a-z, A-Z, 0-9, -, _, and accented letters.")
.insertAfter($("#regusername").parent().parent());
err = true;
}
if(pw == "") {
$("<div/>").addClass("alert alert-error")
.text("Password must not be blank")
.insertAfter($("#regpw").parent().parent());
$("#regpw").parent().parent().addClass("error");
err = true;
}
if(pw != pwc) {
$("<div/>").addClass("alert alert-error")
.text("Passwords do not match")
.insertAfter($("#regpwconfirm").parent().parent());
$("#regpwconfirm").parent().parent().addClass("error");
err = true;
}
if(err) {
return;
}
$("#registerbtn").attr("disabled", true);
// Input valid, try registering
var data = {
name: name,
pw: pw
};
postJSON(WEB_URL + "/api/register?callback=?", data, function (data) {
$("#registerbtn").attr("disabled", false);
if(data.success) {
uname = name;
session = data.session;
onLogin();
$("<div/>").addClass("alert alert-success")
.text("Registration successful")
.insertBefore($("#registerpane form"));
$("#regpw").val("");
$("#regusername").val("");
}
else {
$("<div/>").addClass("alert alert-error")
.text(data.error)
.insertBefore($("#registerpane form"));
}
});
});
$("#loginbtn").click(function() {
$("#loginpane").find(".alert-error").remove();
$("#loginpane").find(".alert-success").remove();
if($("#loginpw").val() == "") {
$("<div/>").addClass("alert alert-error")
.text("Please provide a password")
.insertAfter($("#loginpw").parent().parent());
$("#loginpw").parent().parent().addClass("error");
return;
}
uname = $("#loginusername").val();
var data = {
name: uname,
pw: $("#loginpw").val()
};
postJSON(WEB_URL+"/api/login?callback=?", data, function(data) {
if(data.success) {
session = data.session;
onLogin();
$("<div/>").addClass("alert alert-success")
.text("Login successful")
.insertBefore($("#loginpane form"));
$("#loginpw").val("");
$("#loginusername").val("");
}
else {
$("<div/>").addClass("alert alert-error")
.text(data.error)
.insertBefore($("#loginpane form"));
}
});
});
$("#cpwbtn").click(function() {
$("#changepwpane").find(".alert-error").remove();
$("#changepwpane").find(".alert-success").remove();
$("#changepwpane").find(".error").removeClass("error");
var name = $("#cpwusername").val();
var oldpw = $("#cpwoldpw").val();
var newpw = $("#cpwnewpw").val();
var newpwc = $("#cpwconfirm").val();
var err = false;
if(oldpw == "") {
$("<div/>").addClass("alert alert-error")
.text("Password must not be empty")
.insertAfter($("#cpwoldpw").parent().parent());
$("#cpwoldpw").parent().parent().addClass("error");
err = true;
}
if(newpw == "") {
$("<div/>").addClass("alert alert-error")
.text("Password must not be empty")
.insertAfter($("#cpwnewpw").parent().parent());
$("#cpwnewpw").parent().parent().addClass("error");
err = true;
}
if(newpw != newpwc) {
$("<div/>").addClass("alert alert-error")
.text("Passwords do not match")
.insertAfter($("#cpwconfirm").parent().parent());
$("#cpwconfirm").parent().parent().addClass("error");
err = true;
}
if(err) {
return;
}
// Input valid, try changing password
var data = {
name: name,
oldpw: oldpw,
newpw: newpw
};
postJSON(WEB_URL + "/api/account/passwordchange?callback=?", data,
function (data) {
if(data.success) {
$("<div/>").addClass("alert alert-success")
.text("Password changed.")
.insertBefore($("#changepwpane form"));
uname = name;
session = data.session;
onLogin();
}
else {
$("<div/>").addClass("alert alert-error")
.text(data.error)
.insertBefore($("#changepwpane form"));
}
});
});
$("#cebtn").click(function() {
$("#changeemailpane").find(".alert-error").remove();
$("#changeemailpane").find(".alert-success").remove();
var name = $("#ceusername").val();
var pw = $("#cepw").val();
var email = $("#ceemail").val();
if(pw == "") {
$("<div/>").addClass("alert alert-error")
.text("Please provide a password")
.insertAfter($("#cepw").parent().parent());
$("#cepw").parent().parent().addClass("error");
return;
}
if(!email.match(/^[\w_\.]+@[\w_\.]+[a-zA-Z]+$/)) {
$("<div/>").addClass("alert alert-error")
.text("Invalid email")
.insertAfter($("#ceemail").parent().parent());
$("#ceemail").parent().parent().addClass("error");
return;
}
if(email.match(/.*@(localhost|127\.0\.0\.1)/i)) {
$("<div/>").addClass("alert alert-error")
.text("Nice try, but no.")
.insertAfter($("#ceemail").parent().parent());
$("#ceemail").parent().parent().addClass("error");
return;
}
var data = {
name: name,
pw: pw,
email: email
};
postJSON(WEB_URL + "/api/account/email?callback=?", data,
function (data) {
if(data.success) {
$("<div/>").addClass("alert alert-success")
.text("Email updated")
.insertBefore($("#changeemailpane form"));
uname = name;
session = data.session;
onLogin();
}
else {
$("<div/>").addClass("alert alert-error")
.text(data.error)
.insertBefore($("#changeemailpane form"));
}
});
});
$("#rpbtn").click(function() {
$("#rpbtn").text("Sending...");
$("#pwresetpane").find(".alert-error").remove();
$("#pwresetpane").find(".alert-success").remove();
var name = $("#rpusername").val();
var email = $("#rpemail").val();
var data = {
name: name,
email: email
};
postJSON(WEB_URL + "/api/account/passwordreset?callback=?", data,
function (data) {
$("#rpbtn").text("Send Reset");
if(data.success) {
$("<div/>").addClass("alert alert-success")
.text("Password reset link issued. Check your email.")
.insertBefore($("#pwresetpane form"));
}
else {
$("<div/>").addClass("alert alert-error")
.text(data.error)
.insertBefore($("#pwresetpane form"));
}
});
});
$("#profilesave").click(function() {
$("#profilepane").find(".alert-error").remove();
$("#profilepane").find(".alert-success").remove();
var img = $("#profileimg").val();
var text = $("#profiletext").val();
var data = {
name: uname,
session: session,
profile_image: img,
profile_text: text
};
postJSON(WEB_URL+"/api/account/profile?callback=?", data,
function (data) {
if(data.success) {
$("<div/>").addClass("alert alert-success")
.text("Profile updated.")
.insertBefore($("#profilepane form"));
}
else {
$("<div/>").addClass("alert alert-error")
.text(data.error)
.insertBefore($("#profilepane form"));
}
});
});
$("#login").click(function() {
if(!loggedin) {
makeTabCallback("#login", "#loginpane")();
}
else {
uname = "";
session = "";
eraseCookie("cytube_uname");
eraseCookie("cytube_session");
$("#accountnav li")[0].innerHTML = "Not Logged In";
$("#register").show();
$("#login").text("Login");
loggedin = false;
}
});

View File

@ -1,551 +0,0 @@
var AUTH = "";
var NO_WEBSOCKETS = false;
/* init socket connection */
$.getScript(IO_URL+"/socket.io/socket.io.js", function() {
try {
if(NO_WEBSOCKETS) {
var i = io.transports.indexOf("websocket");
if(i >= 0)
io.transports.splice(i, 1);
}
socket = io.connect(IO_URL);
setupCallbacks();
}
catch(e) {
Callbacks.disconnect();
}
});
$("#login").click(showLoginMenu);
$("#logout").click(function() {
eraseCookie("cytube_uname");
eraseCookie("cytube_session");
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)
var sort_field = tbl.data("sortby");
var sort_desc = tbl.data("sort_desc");
var p = tbl.data("paginator");
if(sort_field) {
p.items.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;
});
}
p.loadPage(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_chanlookup", "#chanlookup");
$("#chanlookup_id").click(function() {
tableResort($("#chanlookup table"), "id");
});
$("#chanlookup_name").click(function() {
tableResort($("#chanlookup table"), "name");
});
$("#chanlookup_owner").click(function() {
tableResort($("#chanlookup table"), "owner");
});
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(function () {
socket.emit("acp-actionlog-list");
});
$("#actionlog_filter").click(getActionLog);
$("#actionlog_searchbtn").click(function() {
var tbl = $("#actionlog table");
var sfield = $("#actionlog_sfield").val();
var sval = $("#actionlog_search").val().toLowerCase();
var sort = $("#actionlog_sort").val();
var desc = $("#actionlog_sortorder").val() === "true";
tbl.data("sort_desc", desc);
tbl.data("sortby", sort);
var entries = tbl.data("allentries");
entries = entries.filter(function (item, i, arr) {
var f = item[sfield];
if(sfield === "time")
f = new Date(f).toString().toLowerCase();
return f.indexOf(sval) > -1;
});
tbl.data("entries", entries);
var p = tbl.data("paginator");
p.items = entries;
tableResort(tbl);
});
$("#actionlog_clear").click(function() {
socket.emit("acp-actionlog-clear", $("#actionlog_filter").val());
socket.emit("acp-actionlog-list");
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");
});
menuHandler("#show_stats", "#stats");
$("#show_stats").click(function () {
socket.emit("acp-view-stats");
});
function reverseLog() {
$("#log").text($("#log").text().split("\n").reverse().join("\n"));
}
$("#log_reverse").click(reverseLog);
function getSyslog() {
$.ajax(WEB_URL+"/api/logging/syslog?"+AUTH).done(function(data) {
$("#log").text(data);
});
}
$("#syslog").click(getSyslog);
function getErrlog() {
$.ajax(WEB_URL+"/api/logging/errorlog?"+AUTH).done(function(data) {
$("#log").text(data);
});
}
$("#errlog").click(getErrlog);
function getActionLog() {
var types = "&actions=" + $("#actionlog_filter").val().join(",");
$.getJSON(WEB_URL+"/api/logging/actionlog?"+AUTH+types+"&callback=?")
.done(function(entries) {
var tbl = $("#actionlog table");
entries.forEach(function (e) {
e.time = parseInt(e.time);
});
var p = tbl.data("paginator");
if(p) {
p.items = entries;
}
else {
var opts = {
preLoadPage: function () {
$("#actionlog tbody").remove();
},
generator: function (e, page, index) {
var tr = $("<tr/>").appendTo($("#actionlog table"));
var rem = $("<td/>").appendTo(tr);
$("<button/>").addClass("btn btn-mini btn-danger")
.html("<i class='icon-trash'></i>")
.appendTo(rem)
.click(function () {
socket.emit("acp-actionlog-clear-one", e);
tr.hide("blind", function () {
tr.remove();
getActionLog();
});
});
$("<td/>").text(e.ip).appendTo(tr);
$("<td/>").text(e.name).appendTo(tr);
$("<td/>").text(e.action).appendTo(tr);
$("<td/>").text(e.args).appendTo(tr);
$("<td/>").text(new Date(e.time).toString()).appendTo(tr);
}
};
p = Paginate(entries, opts);
p.paginator.insertBefore($("#actionlog table"));
tbl.data("paginator", p);
}
tbl.data("sortby", "time");
tbl.data("sort_desc", true);
tbl.data("entries", entries);
tbl.data("allentries", entries);
tableResort(tbl);
});
}
function getChanlog() {
var chan = $("#channame").val();
$.ajax(WEB_URL+"/api/logging/channels/"+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());
});
$("#chanlookup_submit").click(function () {
socket.emit("acp-lookup-channel", {
field: $("#chanlookup_field").val(),
value: $("#chanlookup_value").val()
});
});
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=" + encodeURIComponent(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");
var p = tbl.data("paginator");
if(p) {
p.items = data;
}
else {
var opts = {
preLoadPage: function () {
tbl.find("tbody").remove();
},
generator: function (u, page, index) {
var tr = $("<tr/>").appendTo(tbl);
$("<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));
}
};
p = Paginate(data, opts);
p.paginator.insertBefore(tbl);
tbl.data("paginator", p);
}
tbl.data("sortby", "uname");
tbl.data("sort_desc", false);
tableResort(tbl);
});
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: /reset.html?" + data.hash);
});
socket.on("acp-channeldata", function(data) {
var tbl = $("#chanlookup table");
var p = tbl.data("paginator");
if(p) {
p.items = data;
}
else {
var opts = {
preLoadPage: function () {
tbl.find("tbody").remove();
},
generator: function (u, page, index) {
var tr = $("<tr/>").appendTo(tbl);
$("<td/>").text(u.id).appendTo(tr);
$("<td/>").text(u.name).appendTo(tr);
$("<td/>").text(u.owner).appendTo(tr);
}
};
p = Paginate(data, opts);
p.paginator.insertBefore(tbl);
tbl.data("paginator", p);
}
tbl.data("sortby", "id");
tbl.data("sort_desc", false);
tableResort(tbl);
});
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);
});
socket.on("acp-view-stats", function (stats) {
var labels = [];
var ucounts = [];
var ccounts = [];
var mcounts = [];
var lastdate = "";
stats.forEach(function (s) {
var d = new Date(parseInt(s.time));
var t = "";
if(d.toDateString() !== lastdate) {
lastdate = d.toDateString();
t = d.getFullYear()+"-"+(d.getMonth()+1)+"-"+d.getDate();
t += " " + d.toTimeString().split(" ")[0];
}
else {
t = d.toTimeString().split(" ")[0];
}
labels.push(t);
ucounts.push(s.usercount);
ccounts.push(s.chancount);
mcounts.push(s.mem / 1000000);
});
var user_data = {
labels: labels,
datasets: [
{
fillColor: "rgba(151, 187, 205, 0.5)",
strokeColor: "rgba(151, 187, 205, 1)",
pointColor: "rgba(151, 187, 205, 1)",
pointStrokeColor: "#fff",
data: ucounts
}
]
};
var chan_data = {
labels: labels,
datasets: [
{
fillColor: "rgba(151, 187, 205, 0.5)",
strokeColor: "rgba(151, 187, 205, 1)",
pointColor: "rgba(151, 187, 205, 1)",
pointStrokeColor: "#fff",
data: ccounts
}
]
};
var mem_data = {
labels: labels,
datasets: [
{
fillColor: "rgba(151, 187, 205, 0.5)",
strokeColor: "rgba(151, 187, 205, 1)",
pointColor: "rgba(151, 187, 205, 1)",
pointStrokeColor: "#fff",
data: mcounts
}
]
};
new Chart($("#stat_users")[0].getContext("2d")).Line(user_data);
new Chart($("#stat_channels")[0].getContext("2d")).Line(chan_data);
new Chart($("#stat_mem")[0].getContext("2d")).Line(mem_data);
});
socket.on("acp-actionlog-list", function (alist) {
$("#actionlog_filter").html("");
alist.sort(function(a, b) {
return a == b ? 0 : (a < b ? -1 : 1);
});
alist.forEach(function(a) {
$("<option/>").text(a).val(a).appendTo($("#actionlog_filter"));
});
});
}

View File

@ -1,247 +0,0 @@
/* =========================================================
* bootstrap-modal.js v2.3.1
* http://twitter.github.com/bootstrap/javascript.html#modals
* =========================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ========================================================= */
!function ($) {
"use strict"; // jshint ;_;
/* MODAL CLASS DEFINITION
* ====================== */
var Modal = function (element, options) {
this.options = options
this.$element = $(element)
.delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this))
this.options.remote && this.$element.find('.modal-body').load(this.options.remote)
}
Modal.prototype = {
constructor: Modal
, toggle: function () {
return this[!this.isShown ? 'show' : 'hide']()
}
, show: function () {
var that = this
, e = $.Event('show')
this.$element.trigger(e)
if (this.isShown || e.isDefaultPrevented()) return
this.isShown = true
this.escape()
this.backdrop(function () {
var transition = $.support.transition && that.$element.hasClass('fade')
if (!that.$element.parent().length) {
that.$element.appendTo(document.body) //don't move modals dom position
}
that.$element.show()
if (transition) {
that.$element[0].offsetWidth // force reflow
}
that.$element
.addClass('in')
.attr('aria-hidden', false)
that.enforceFocus()
transition ?
that.$element.one($.support.transition.end, function () { that.$element.focus().trigger('shown') }) :
that.$element.focus().trigger('shown')
})
}
, hide: function (e) {
e && e.preventDefault()
var that = this
e = $.Event('hide')
this.$element.trigger(e)
if (!this.isShown || e.isDefaultPrevented()) return
this.isShown = false
this.escape()
$(document).off('focusin.modal')
this.$element
.removeClass('in')
.attr('aria-hidden', true)
$.support.transition && this.$element.hasClass('fade') ?
this.hideWithTransition() :
this.hideModal()
}
, enforceFocus: function () {
var that = this
$(document).on('focusin.modal', function (e) {
if (that.$element[0] !== e.target && !that.$element.has(e.target).length) {
that.$element.focus()
}
})
}
, escape: function () {
var that = this
if (this.isShown && this.options.keyboard) {
this.$element.on('keyup.dismiss.modal', function ( e ) {
e.which == 27 && that.hide()
})
} else if (!this.isShown) {
this.$element.off('keyup.dismiss.modal')
}
}
, hideWithTransition: function () {
var that = this
, timeout = setTimeout(function () {
that.$element.off($.support.transition.end)
that.hideModal()
}, 500)
this.$element.one($.support.transition.end, function () {
clearTimeout(timeout)
that.hideModal()
})
}
, hideModal: function () {
var that = this
this.$element.hide()
this.backdrop(function () {
that.removeBackdrop()
that.$element.trigger('hidden')
})
}
, removeBackdrop: function () {
this.$backdrop && this.$backdrop.remove()
this.$backdrop = null
}
, backdrop: function (callback) {
var that = this
, animate = this.$element.hasClass('fade') ? 'fade' : ''
if (this.isShown && this.options.backdrop) {
var doAnimate = $.support.transition && animate
this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
.appendTo(document.body)
this.$backdrop.click(
this.options.backdrop == 'static' ?
$.proxy(this.$element[0].focus, this.$element[0])
: $.proxy(this.hide, this)
)
if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
this.$backdrop.addClass('in')
if (!callback) return
doAnimate ?
this.$backdrop.one($.support.transition.end, callback) :
callback()
} else if (!this.isShown && this.$backdrop) {
this.$backdrop.removeClass('in')
$.support.transition && this.$element.hasClass('fade')?
this.$backdrop.one($.support.transition.end, callback) :
callback()
} else if (callback) {
callback()
}
}
}
/* MODAL PLUGIN DEFINITION
* ======================= */
var old = $.fn.modal
$.fn.modal = function (option) {
return this.each(function () {
var $this = $(this)
, data = $this.data('modal')
, options = $.extend({}, $.fn.modal.defaults, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('modal', (data = new Modal(this, options)))
if (typeof option == 'string') data[option]()
else if (options.show) data.show()
})
}
$.fn.modal.defaults = {
backdrop: true
, keyboard: true
, show: true
}
$.fn.modal.Constructor = Modal
/* MODAL NO CONFLICT
* ================= */
$.fn.modal.noConflict = function () {
$.fn.modal = old
return this
}
/* MODAL DATA-API
* ============== */
$(document).on('click.modal.data-api', '[data-toggle="modal"]', function (e) {
var $this = $(this)
, href = $this.attr('href')
, $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) //strip for ie7
, option = $target.data('modal') ? 'toggle' : $.extend({ remote:!/#/.test(href) && href }, $target.data(), $this.data())
e.preventDefault()
$target
.modal(option)
.one('hide', function () {
$this.focus()
})
})
}(window.jQuery);

View File

@ -1,60 +0,0 @@
/* ===================================================
* bootstrap-transition.js v2.3.1
* http://twitter.github.com/bootstrap/javascript.html#transitions
* ===================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ========================================================== */
!function ($) {
"use strict"; // jshint ;_;
/* CSS TRANSITION SUPPORT (http://www.modernizr.com/)
* ======================================================= */
$(function () {
$.support.transition = (function () {
var transitionEnd = (function () {
var el = document.createElement('bootstrap')
, transEndEventNames = {
'WebkitTransition' : 'webkitTransitionEnd'
, 'MozTransition' : 'transitionend'
, 'OTransition' : 'oTransitionEnd otransitionend'
, 'transition' : 'transitionend'
}
, name
for (name in transEndEventNames){
if (el.style[name] !== undefined) {
return transEndEventNames[name]
}
}
}())
return transitionEnd && {
end: transitionEnd
}
})()
})
}(window.jQuery);

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -1,237 +0,0 @@
(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");
});
clickHandler("#show_chanlog", "#chanlog");
$("#show_chanlog").click(function () {
socket.emit("readChanLog");
});
$("#chanlog_refresh").click(function () {
socket.emit("readChanLog");
});
genPermissionsEditor();
$("#chanopts_submit").click(function() {
var hms = $("#opt_maxlength").val().split(":");
var len = 0;
if(hms.length == 3) {
len = parseInt(hms[0]) * 3600 + parseInt(hms[1]) * 60 + parseInt(hms[2]);
}
else if(hms.length == 2) {
len = parseInt(hms[0]) * 60 + parseInt(hms[1]);
}
else {
len = parseInt(hms[0]);
}
var sus = parseFloat($("#opt_chat_antiflood_sustained").val()) || 0;
if (sus <= 0) {
sus = 1;
}
socket.emit("setOptions", {
allow_voteskip: $("#opt_allow_voteskip").prop("checked"),
voteskip_ratio: parseFloat($("#opt_voteskip_ratio").val()),
maxlength: len,
pagetitle: $("#opt_pagetitle").val() || CHANNEL.name,
externalcss: $("#opt_externalcss").val(),
externaljs: $("#opt_externaljs").val(),
chat_antiflood: $("#opt_chat_antiflood").prop("checked"),
chat_antiflood_params: {
burst: $("#opt_chat_antiflood_burst").val(),
sustained: $("#opt_chat_antiflood_sustained").val()
},
show_public: $("#opt_show_public").prop("checked"),
password: $("#opt_password").val(),
enable_link_regex: $("#opt_enable_link_regex").prop("checked"),
afk_timeout: parseInt($("#opt_afktimeout").val())
});
});
$("#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("");
});
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;
}
$("#multifiltersubmit").click(function () {
var lines = $("#multifiltereditor").val().split("\n");
for(var i in lines) {
var ln = lines[i];
var fields = splitreEntry(ln);
if(fields.length < 3 || fields.length > 4) {
makeAlert("Error on line "+(parseInt(i)+1)+". Format: name regex flags replacement", "alert-error")
.insertBefore($("#multifiltereditor"));
return;
}
var name = "", re = "", f = "", replace = "";
if(fields.length == 3) {
name = fields[0];
re = fields[0];
f = fields[1];
replace = fields[2];
}
else if(fields.length == 4) {
name = fields[0];
re = fields[1];
f = fields[2];
replace = fields[3];
}
socket.emit("updateFilter", {
name: name,
source: re,
flags: f,
replace: replace,
filterlinks: false,
active: true
});
}
});
var logfilters = [
"#filter_chat",
"#filter_polls",
"#filter_queue",
"#filter_bans",
"#filter_channelsettings",
"#filter_joinquit"
];
logfilters.unshift("#filter_all");
logfilters.forEach(function (f) {
$(f).change(function () {
if (f !== "#filter_all") {
$("#filter_all").prop("checked", false);
}
filterChannelLog();
});
});
logfilters.shift();
})();

View File

@ -1,4 +0,0 @@
var Froogaloop=function(){function e(a){return new e.fn.init(a)}function h(a,c,b){if(!b.contentWindow.postMessage)return!1;var f=b.getAttribute("src").split("?")[0],a=JSON.stringify({method:a,value:c});"//"===f.substr(0,2)&&(f=window.location.protocol+f);b.contentWindow.postMessage(a,f)}function j(a){var c,b;try{c=JSON.parse(a.data),b=c.event||c.method}catch(f){}"ready"==b&&!i&&(i=!0);if(a.origin!=k)return!1;var a=c.value,e=c.data,g=""===g?null:c.player_id;c=g?d[g][b]:d[b];b=[];if(!c)return!1;void 0!==
a&&b.push(a);e&&b.push(e);g&&b.push(g);return 0<b.length?c.apply(null,b):c.call()}function l(a,c,b){b?(d[b]||(d[b]={}),d[b][a]=c):d[a]=c}var d={},i=!1,k="";e.fn=e.prototype={element:null,init:function(a){"string"===typeof a&&(a=document.getElementById(a));this.element=a;a=this.element.getAttribute("src");"//"===a.substr(0,2)&&(a=window.location.protocol+a);for(var a=a.split("/"),c="",b=0,f=a.length;b<f;b++){if(3>b)c+=a[b];else break;2>b&&(c+="/")}k=c;return this},api:function(a,c){if(!this.element||
!a)return!1;var b=this.element,f=""!==b.id?b.id:null,d=!c||!c.constructor||!c.call||!c.apply?c:null,e=c&&c.constructor&&c.call&&c.apply?c:null;e&&l(a,e,f);h(a,d,b);return this},addEvent:function(a,c){if(!this.element)return!1;var b=this.element,d=""!==b.id?b.id:null;l(a,c,d);"ready"!=a?h("addEventListener",a,b):"ready"==a&&i&&c.call(null,d);return this},removeEvent:function(a){if(!this.element)return!1;var c=this.element,b;a:{if((b=""!==c.id?c.id:null)&&d[b]){if(!d[b][a]){b=!1;break a}d[b][a]=null}else{if(!d[a]){b=
!1;break a}d[a]=null}b=!0}"ready"!=a&&b&&h("removeEventListener",a,c)}};e.fn.init.prototype=e.fn;window.addEventListener?window.addEventListener("message",j,!1):window.attachEvent("onmessage",j);return window.Froogaloop=window.$f=e}();

View File

@ -1,21 +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 IO_URL = "http://localhost:1337";
var WEB_URL = "http://localhost:8080";
var SSL_URL = "https://localhost:443";
var ALLOW_SSL = false;
if (ALLOW_SSL) {
if (location.protocol === "https:" || USEROPTS.secure_connection) {
IO_URL = WEB_URL = SSL_URL;
}
}

9597
www/assets/js/jquery.js vendored

File diff suppressed because it is too large Load Diff

View File

@ -1,133 +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 NotWebsocket = function() {
this.connected = false;
this.polltmr = false;
this.nws = true;
$.getJSON(WEB_URL + "/nws/connect?callback=?", function(data) {
this.hash = data;
this.connected = true;
this.recv(["connect", undefined]);
this.pollint = 100;
this.pollonce();
}.bind(this));
this.handlers = {};
}
NotWebsocket.prototype.pollonce = function() {
if(this.polltmr !== false)
clearTimeout(this.polltmr);
if(!this.connected)
return;
this.poll();
this.polltmr = setTimeout(function() {
this.pollonce();
}.bind(this), this.pollint);
}
NotWebsocket.prototype.poll = function() {
if(!this.connected)
return;
if(this.polling)
return;
$.getJSON(WEB_URL+"/nws/"+this.hash+"/poll?callback=?", function(data) {
this.polling = true;
// Adaptive polling rate
// Poll every 1000ms if no activity
// every 500ms if minor activity
// every 100ms is very active
if(data.length == 0) {
this.pollint = 1000;
}
else if(data.length < 10 && this.pollint < 500) {
this.pollint += 100;
}
else if(data.length > 10) {
this.pollint = 100;
}
for(var i = 0; i < data.length; i++) {
try {
this.recv(data[i]);
}
catch(e) { }
}
this.polling = false;
}.bind(this))
.fail(function() {
this.disconnect();
}.bind(this));
}
NotWebsocket.prototype.emit = function(msg, data) {
if(!this.connected) {
setTimeout(function() {
this.emit(msg, data);
}.bind(this), 100);
return;
}
var pkt = [msg, data];
var str = encodeURIComponent(JSON.stringify(pkt)).replace(/\//g, "%2F");
$.getJSON(WEB_URL+"/nws/"+this.hash+"/"+str+"?callback=?", function() {
// Poll more quickly because sending a packet usually means
// expecting some data to come back
this.pollint = 100;
this.pollonce();
}.bind(this));
}
NotWebsocket.prototype.reconnect = function() {
$.getJSON(WEB_URL + "/nws/connect", function(data) {
this.hash = data;
this.connected = true;
this.recv(["connect", undefined]);
this.pollint = setInterval(function() {
this.poll();
}.bind(this), 100);
}.bind(this))
.fail(function() {
if(this.reconndelay < 10000)
this.reconndelay += 500;
setTimeout(function() {
this.reconnect();
}.bind(this), this.reconndelay);
}.bind(this));
}
NotWebsocket.prototype.on = function(msg, callback) {
if(!(msg in this.handlers))
this.handlers[msg] = [];
this.handlers[msg].push(callback);
}
NotWebsocket.prototype.recv = function(pkt) {
var msg = pkt[0], data = pkt[1];
if(!(msg in this.handlers)) {
return;
}
for(var i = 0; i < this.handlers[msg].length; i++) {
this.handlers[msg][i](data);
}
}
NotWebsocket.prototype.disconnect = function() {
this.recv(["disconnect", undefined]);
if(this.polltmr !== false)
clearTimeout(this.polltmr);
this.polltmr = false;
this.connected = false;
this.reconndelay = 1000;
setTimeout(function() {
this.reconnect();
}.bind(this), this.reconndelay);
}

View File

@ -1,278 +0,0 @@
<!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="//code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" rel="stylesheet">
<style>
body {
padding-top: 60px;
}
@media (max-width: 979px) {
body {
padding-top: 0!important;
}
}
</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="https://github.com/calzoneman/sync/wiki/Beginner%27s-Guide-and-FAQ" target="_blank">Help</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="chatonly">Chat Only</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" id="motdwrap">
<button class="close pull-right" id="togglemotd"><i class="icon-minus" title="Show/Hide MOTD"></i></button>
<button class="close pull-right" id="editmotd" style="display: none"><i class="icon-file" title="Edit MOTD"></i></button>
<div id="motd"></div>
</div>
<div id="drinkbar">
<h1 id="drinkcount"></h1>
</div>
</div>
<!-- announcement area -->
<div class="row-fluid" id="announcements">
</div>
<!-- main row -->
<div class="row" id="main">
<div class="span12" id="main-outer">
<div class="row" id="main-inner">
<!-- chat container -->
<div class="span5" id="chatwrap">
<!-- user count -->
<div id="usercountwrap">
<i class="icon-chevron-up pull-left pointer" id="userlisttoggle" title="Show/Hide Userlist"></i>
<span id="usercount" class="pointer">Not connected</span>
<span id="adminflair" class="pull-right label label-default pointer" style="display: none">A</span>
<span id="modflair" class="pull-right label label-default pointer" style="display: none">M</span>
</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>
</div>
</div>
<!-- playlist row -->
<div class="row" id="playlistrow">
<div class="span12" id="playlist-outer">
<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/YouTube Search</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>
<ul class="span12 videolist" id="library">
</ul>
</div>
</div>
<!-- user playlists -->
<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 class="span5">
<button class="btn btn-block" id="userpl_save">Save Playlist</button>
</div>
<ul class="span12" id="userpl_list">
</ul>
</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_auto" href="javascript:void(0)">Quality: Auto</a></li>
<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>
<button class="btn btn-small" id="mediarefresh">Refresh Media</button>
</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 class="span12">
<button id="customembed_btn" class="btn btn-block" style="display: none">Custom Embed</button>
<div id="customembed_entry" style="display: none;">
Paste the embed code below and hit Next or End. Acceptable embed codes are <code>&lt;iframe&gt;</code> and <code>&lt;object&gt;</code> tags.
<textarea rows="3" class="input-block-level" id="customembed_code"></textarea>
<input type="text" class="input-block-level" id="customembed_title" placeholder="(Optional) Title">
</div>
<br>
</div>
<div id="extended_controls" class="span12">
<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: 46%" id="voteskip">Voteskip</button>
<button class="btn" style="width: 46%" id="getplaylist">Get Playlist URLs</button>
<button class="btn btn-danger" style="width: 8%" id="qlockbtn" title="Playlist Locked">
<i class="icon-lock"></i>
</button>
</div>
<!-- queuefail area -->
<div id="queuefail" class="span12">
</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>
<!-- moderator controls -->
<!-- there has got to be a better way to nest this -->
<div class="span12" id="channelsettingswrap3">
<div class="well" id="channelsettingswrap2">
<div class="row-fluid" id="channelsettingswrap">
</div>
</div>
</div>
</div>
<div class="row" id="resizewrap">
<div class="span5"></div>
<div class="span7" id="videowidth"></div>
</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="https://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="//code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
<!-- My Javascript -->
<script src="./assets/js/data.js"></script>
<script src="./assets/js/iourl.js"></script>
<script src="./assets/js/player.js"></script>
<script src="./assets/js/notwebsocket.js"></script>
<script src="./assets/js/paginator.js"></script>
<script src="./assets/js/util.js"></script>
<script src="./assets/js/ui.js"></script>
<script src="./assets/js/callbacks.js"></script>
<!-- APIs -->
<script defer src="https://www.youtube.com/iframe_api"></script>
<script defer src="//api.dmcdn.net/all.js"></script>
<script defer src="./assets/js/jwplayer.js"></script>
<script defer src="./assets/js/sc.js"></script>
<script defer src="./assets/js/froogaloop.min.js"></script>
<script defer 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

@ -1,186 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>CyTube Channel Listing</title>
<link href="assets/css/bootstrap.css" rel="stylesheet">
<style type="text/css">
#channeldata td, #channeldata th {
text-align: center;
vertical-align: middle;
}
.loginform {
margin: 100px auto 20px;
padding: 19px 29px 29px;
border-radius: 5px 5px 5px 5px;
border: 1px solid #dedede;
max-width: 300px;
}
</style>
</head>
<body>
<div class="container">
<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>
<table id="channeldata" class="table table-striped table-bordered">
<thead>
<tr>
<th>Channel</th>
<th>Connected</th>
<th>Playing</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<script src="assets/js/jquery.js" type="text/javascript"></script>
<script src="assets/js/iourl.js" type="text/javascript"></script>
<script type="text/javascript">
var session = readCookie("cytube_session") || "";
var uname = readCookie("cytube_uname") || "";
var p = "";
if(uname && session) {
$.getJSON(WEB_URL+"/api/json/login?name="+encodeURIComponent(uname)+"&session="+session+"&callback=?", function(data) {
if(data.success) {
$(".loginform").remove();
createCookie("cytube_uname", uname, 7);
createCookie("cytube_session", session, 7);
p = "name=" + uname + "&session=" + session;
refresh();
setInterval(refresh, 5000);
}
});
}
var q = "";
$("#login").click(function() {
uname = $("#name").val();
q = "name=" + encodeURIComponent($("#name").val())
+ "&pw=" + encodeURIComponent($("#pw").val());
$.getJSON(WEB_URL+"/api/json/login?"+q+"&callback=?", function(data) {
if(data.success) {
$(".loginform").remove();
session = data.session;
createCookie("cytube_uname", uname, 7);
createCookie("cytube_session", session, 7);
p = "name=" + uname + "&session=" + session;
}
});
refresh();
setInterval(refresh, 5000);
});
function idToURL(data) {
var entry = "";
switch(data.type) {
case "yt":
entry = "http://youtube.com/watch?v="+data.id;
break;
case "vi":
entry = "http://vimeo.com/"+data.id;
break;
case "dm":
entry = "http://dailymotion.com/video/"+data.id;
break;
case "sc":
entry = data.id;
break;
case "li":
entry = "http://livestream.com/"+data.id;
break;
case "tw":
entry = "http://twitch.tv/"+data.id;
break;
case "rt":
case "jw":
entry = data.id;
break;
default:
break;
}
return entry;
}
function refresh() {
$.getJSON(WEB_URL+"/api/json/listloaded?"+p+"&callback=?", function(data) {
var host = document.location + "";
host = host.substring(0, host.indexOf("/widget.html"));
if(data.error) {
alert(data.error);
return;
}
$(".loginform").remove();
$("#channeldata").find("tbody").remove();
data.sort(function(a, b) {
var x = a.usercount;
var y = b.usercount;
if(x == y) {
var c = a.name.toLowerCase();
var d = b.name.toLowerCase();
return c == d ? 0 : (c < d ? -1 : 1);
}
return y - x;
});
var total = 0;
for(var i = 0; i < data.length; i++) {
var d = data[i];
var tr = $("<tr/>").appendTo($("#channeldata"));
var name = $("<td/>").appendTo(tr);
$("<a/>").attr("href", host + "/r/" + d.name)
.text(d.pagetitle + " (" + d.name + ")")
.appendTo(name);
$("<td/>").text(d.usercount || 0).appendTo(tr);
var title = $("<td/>").appendTo(tr);
if(d.media.id) {
$("<a/>").appendTo(title)
.text(d.media.title)
.attr("href", idToURL(d.media))
.attr("target", "_blank");
}
else {
title.text("-");
}
total += (d.usercount || 0);
}
var tr = $("<tr/>").appendTo($("#channeldata"));
$("<td/>").text("Total").appendTo(tr);
$("<td/>").text(total).appendTo(tr);
$("<td/>").appendTo(tr);
});
}
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>
</html>

View File

@ -1,279 +0,0 @@
<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>
<li id="chanlog_tab"><a href="javascript:void(0);" id="show_chanlog">Channel Log</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>
<!-- chat flood burst -->
<div class="control-group">
<label class="control-label" for="opt_chat_antiflood_burst" title="The number of messages someone can send with no restriction before the rate limit is applied"># of messages allowed before throttling</label>
<div class="controls">
<input type="text" id="opt_chat_antiflood_burst">
</div>
</div>
<!-- chat flood sustained -->
<div class="control-group">
<label class="control-label" for="opt_chat_antiflood_sustained" title="The number of messages (after the burst is exceeded) per second a non-moderator can send"># of messages allowed per second (sustained)</label>
<div class="controls">
<input type="text" id="opt_chat_antiflood_sustained">
</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>
<!-- max video length -->
<div class="control-group">
<label class="control-label" for="opt_maxlength">Maximum Video Length</label>
<div class="controls">
<input type="text" id="opt_maxlength" placeholder="HH:MM:SS">
</div>
</div>
<!-- auto afk -->
<div class="control-group">
<label class="control-label" for="opt_afktimeout">Auto AFK Delay (0 to disable)</label>
<div class="controls">
<input type="text" id="opt_afktimeout" placeholder="0">
</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>
<!-- password -->
<div class="control-group">
<label class="control-label" for="opt_password">Password (blank to disable)</label>
<div class="controls">
<input type="text" id="opt_password">
</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">
<strong>Notice:</strong>
The MOTD can now be edited in-place by clicking the small page icon next to the minus/plus icon. This editor is left in place for legacy purposes.
<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>
<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>
<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>
<p>Multi-Filter Editor (format: name regex flags replacement). Only use this if you know what you are doing</p>
<textarea id="multifiltereditor" class="input-block-level" rows="10"></textarea>
<button class="btn btn-primary" id="multifiltersubmit">Add/Update</button>
</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>
<div id="chanlog" class="span12">
<button class="btn" id="chanlog_refresh">Refresh</button>
<br>
<br>
<strong>Filter Log:</strong>
<form class="form-inline" action="javascript:void(0)">
<label class="checkbox" for="filter_all">Everything
<input type="checkbox" id="filter_all" checked="checked">
</label>
<label class="checkbox" for="filter_chat">Chat
<input type="checkbox" id="filter_chat">
</label>
<label class="checkbox" for="filter_polls">Polls
<input type="checkbox" id="filter_polls">
</label>
<label class="checkbox" for="filter_queue">Playlist actions
<input type="checkbox" id="filter_queue">
</label>
<label class="checkbox" for="filter_bans">Bans
<input type="checkbox" id="filter_bans">
</label>
<label class="checkbox" for="filter_channelsettings">Channel settings
<input type="checkbox" id="filter_channelsettings">
</label>
<label class="checkbox" for="filter_joinquit">Join/Quit Messages
<input type="checkbox" id="filter_joinquit">
</label>
</form>
<pre id="chanlog_contents" style="max-height: 400px; overflow-y: scroll"></pre>
</div>

View File

@ -1 +0,0 @@
<p style="text-align: center"></p>

View File

@ -1,190 +0,0 @@
<!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="">
<meta name="author" content="Calvin 'calzoneman' Montgomery">
<link href="./assets/css/bootstrap.css" rel="stylesheet">
<link href="./assets/css/ytsync.css" rel="stylesheet">
<style>
body {
padding-top: 60px; /* 60px to make the container go all the way to the bottom of the topbar */
}
#channeldata td, #channeldata th {
text-align: center;
vertical-align: middle;
}
</style>
<link href="./assets/css/bootstrap-responsive.css" rel="stylesheet">
</head>
<body>
<div class="wrapper">
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a class="brand" href="index.html">CyTube</a>
<div class="">
<ul class="nav">
<li class="active"><a href="index.html">Home</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>
</ul>
</div>
</div>
</div>
</div>
<div class="container">
<div class="row">
<div id="loggedin" class="span6" style="display: none;">
<h3 id="welcome"></h3>
</div>
</div>
<div class="row">
<div class="span8">
<h3>Public Channels</h3>
<table id="channeldata" class="table table-striped table-bordered">
<thead>
<tr>
<th>Channel</th>
<th>Connected</th>
<th>Playing</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
<div class="span4">
<h3>Enter Channel</h3>
<input type="text" class="input-block-level" id="channel" placeholder="Channel Name">
<br>
<div class="alert alert-info">
<strong>Note:</strong> to register a channel, simply enter a channel name that isn't being used. The first person to join will have temporary admin status of that channel, and the channel can be registered by clicking the button on the blue prompt that appears on an unregistered channel.
</div>
</div>
</div>
</div> <!-- /container -->
<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>
<!-- Third party -->
<script src="./assets/js/jquery.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>
<!-- Mine -->
<script src="./assets/js/data.js"></script>
<script src="./assets/js/iourl.js"></script>
<script type="text/javascript">
var host = location.protocol + "//" + location.host + "/";
setInterval(refresh, 10000);
refresh();
function idToURL(data) {
var entry = "";
switch(data.type) {
case "yt":
entry = "http://youtube.com/watch?v="+data.id;
break;
case "vi":
entry = "http://vimeo.com/"+data.id;
break;
case "dm":
entry = "http://dailymotion.com/video/"+data.id;
break;
case "sc":
entry = data.id;
break;
case "li":
entry = "http://livestream.com/"+data.id;
break;
case "tw":
entry = "http://twitch.tv/"+data.id;
break;
case "rt":
case "jw":
entry = data.id;
break;
default:
break;
}
return entry;
}
function refresh() {
$.getJSON(WEB_URL+"/api/allchannels/public?callback=?",
function(data) {
$("#channeldata").find("tbody").remove();
data.sort(function(a, b) {
var x = a.usercount;
var y = b.usercount;
if(x == y) {
var c = a.name.toLowerCase();
var d = b.name.toLowerCase();
return c == d ? 0 : (c < d ? -1 : 1);
}
return y - x;
});
for(var i = 0; i < data.length; i++) {
var d = data[i];
var tr = $("<tr/>").appendTo($("#channeldata"));
var name = $("<td/>").appendTo(tr);
$("<a/>").attr("href", host + "r/" + d.name)
.text(d.pagetitle + " (" + d.name + ")")
.appendTo(name);
$("<td/>").text(d.usercount || 0).appendTo(tr);
var title = $("<td/>").appendTo(tr);
if(d.media.id) {
$("<a/>").appendTo(title)
.text(d.media.title)
.attr("href", idToURL(d.media))
.attr("target", "_blank");
}
else {
title.text("-");
}
}
});
}
$("#channel").keydown(function(ev) {
if(ev.keyCode == 13) {
if(!$("#channel").val().match(/^[a-zA-Z0-9-_]+$/)) {
alert("Invalid channel names. Channel names may contain alphanumeric characters, underscores, and hyphens");
return;
}
document.location = host + "r/" + $("#channel").val();
}
});
</script>
<script type="text/javascript">
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(params["channel"] && params["channel"].match(/^[a-zA-Z0-9]+$/)) {
var host = location.protocol + "//" + location.host + "/";
document.location = host + "r/" + params["channel"];
}
</script>
</body>
</html>

View File

@ -62,7 +62,7 @@ var LASTCHAT = {
var FOCUSED = true;
var PAGETITLE = "CyTube";
var TITLE_BLINK;
var CHATSOUND = new Audio("/assets/sounds/boop.wav");
var CHATSOUND = new Audio("boop.wav");
var KICKED = false;
var NAME = readCookie("cytube_uname");
var SESSION = readCookie("cytube_session");

View File

@ -11,18 +11,14 @@
}
}
console.log("cookie theme=", theme);
if (theme !== "default") {
var cur = document.getElementById("usertheme");
cur.parentNode.removeChild(cur);
console.log('removed');
var css = document.createElement("link");
css.setAttribute("rel", "stylesheet");
css.setAttribute("type", "text/css");
css.setAttribute("href", theme);
css.setAttribute("id", "usertheme");
document.head.appendChild(css);
console.log(css);
}
})();

View File

@ -1,95 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>CyTube - Login</title>
<link rel="stylesheet" href="assets/css/bootstrap.css">
<style type="text/css">
#username, #pw {
width: 95%;
}
.btn {
width: 45%;
}
</style>
</head>
<body>
<form class="form-horizontal" action="javascript:void(0)">
<div class="control-group">
<label class="control-label" for="username">Username</label>
<div class="controls">
<input type="text" id="username">
</div>
</div>
<div class="control-group">
<label class="control-label" for="pw">Password</label>
<div class="controls">
<input type="password" id="pw">
</div>
</div>
<div class="control-group">
<div class="controls">
<button class="btn" id="login">Login</button>
<a class="btn" id="register" target="_blank" href="account.html">Register</a>
</div>
</div>
</form>
<script src="assets/js/jquery.js" type="text/javascript"></script>
<script src="assets/js/data.js" type="text/javascript"></script>
<script src="assets/js/iourl.js" type="text/javascript"></script>
<script type="text/javascript">
/*
So, it turns out that $.post causes Firefox to use a GET request
on cross-site requests. What the hell? I'd understand if they just
made it error instead, but why give me chicken tenders if I ordered a
cheeseburger and act like everything's peachy?
*/
function postJSON(url, data, callback) {
$.ajax(url, {
method: "POST",
crossDomain: true,
data: data,
success: function (data) {
try {
data = data.substring(data.indexOf("{"));
data = data.substring(0, data.lastIndexOf("}") + 1);
data = JSON.parse(data);
callback(data);
} catch(e) {
return;
}
},
dataType: "text"
});
}
var theme = USEROPTS.theme;
if(theme != "default") {
$("<link/>").attr("rel", "stylesheet")
.attr("type", "text/css")
.attr("id", "usertheme")
.attr("href", theme)
.appendTo($("head"));
}
var source;
var respond = function(e) {
if(e.data == "cytube-syn") {
source = e.source;
source.postMessage("cytube-ack", document.location);
}
}
window.addEventListener("message", respond, false);
$("#login").click(function() {
var data = {
name: $("#username").val(),
pw: $("#pw").val()
};
postJSON(WEB_URL+"/api/login", data, function (data) {
data.uname = $("#username").val();
source.postMessage("cytube-login:"+JSON.stringify(data), document.location);
});
});
</script>
</body>
</html>

View File

@ -1,96 +0,0 @@
<!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="">
<meta name="author" content="Calvin 'calzoneman' Montgomery">
<link href="./assets/css/bootstrap.css" rel="stylesheet">
<link href="./assets/css/ytsync.css" rel="stylesheet">
<style>
body {
padding-top: 60px; /* 60px to make the container go all the way to the bottom of the topbar */
}
</style>
<link href="./assets/css/bootstrap-responsive.css" rel="stylesheet">
</head>
<body>
<div class="wrapper">
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a class="brand" href="index.html">CyTube</a>
<div class="">
<ul class="nav">
<li class="active"><a href="index.html">Home</a></li>
<li><a href="help.html">Help</a></li>
</ul>
</div>
</div>
</div>
</div>
<div class="container">
<div class="row">
<div class="span12">
<h3>Account Recovery</h3>
</div>
</div>
</div> <!-- /container -->
<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>
<!-- Third party -->
<script src="./assets/js/jquery.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>
<!-- Mine -->
<script src="./assets/js/data.js"></script>
<script src="./assets/js/iourl.js"></script>
<script type="text/javascript">
var hash = false;
(function() {
var loc = document.location+"";
if(loc.indexOf("?") == -1) {
return;
}
hash = loc.substring(loc.indexOf("?") + 1);
})();
var url = WEB_URL+"/api/account/passwordrecover?hash="+hash+
"&callback=?";
$.getJSON(url, function(data) {
if(data.success) {
$("<div/>").addClass("alert alert-success")
.html([
"Password Reset.<br><strong>Username: </strong>",
data.name,
"<br><strong>Password: </strong>",
data.pw,
" (change this immediately for security)"].join(""))
.appendTo($(".span12")[0]);
}
else {
$("<div/>").addClass("alert alert-error")
.text(data.error)
.appendTo($(".span12")[0]);
}
});
</script>
</body>
</html>

View File

@ -1,29 +0,0 @@
<div class="modal-header">
<button class="close" data-dismiss="modal" aria-hidden="true">
&times;
</button>
<h3>User Options</h3>
<div class="btn-group">
<button class="btn btn-small" id="uopt-btn-general">General</button>
<button class="btn btn-small" id="uopt-btn-playback">Playback</button>
<button class="btn btn-small" id="uopt-btn-chat">Chat</button>
<button class="btn btn-small" id="uopt-btn-mod">Moderator</button>
</div>
</div>
<div class="modal-body">
<div class="uopt-panel" id="uopt-panel-general">
<h4>General Options</h4>
</div>
<div class="uopt-panel" id="uopt-panel-playback">
<h4>Playback Options</h4>
</div>
<div class="uopt-panel" id="uopt-panel-chat">
<h4>Chat Options</h4>
</div>
<div class="uopt-panel" id="uopt-panel-mod">
<h4>Moderator Options</h4>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary pull-right" id="uopt-btn-save">Save</button>
</div>