diff --git a/lib/channel-new.js b/lib/channel-new.js index 22bf7f67..0e61eaf6 100644 --- a/lib/channel-new.js +++ b/lib/channel-new.js @@ -2128,22 +2128,30 @@ Channel.prototype.handleToggleLock = function (user) { * Updates a chat filter, or adds a new one if the filter does not exist */ Channel.prototype.updateFilter = function (filter) { + var self = this; + if (!filter.name) { filter.name = filter.source; } var found = false; - for (var i = 0; i < this.filters.length; i++) { - if (this.filters[i].name === filter.name) { + for (var i = 0; i < self.filters.length; i++) { + if (self.filters[i].name === filter.name) { found = true; - this.filters[i] = filter; + self.filters[i] = filter; break; } } if (!found) { - this.filters.push(filter); + self.filters.push(filter); } + + self.users.forEach(function (u) { + if (self.hasPermission(u, "filteredit")) { + u.socket.emit("updateChatFilter", filter); + } + }); }; /** @@ -2186,9 +2194,16 @@ Channel.prototype.handleUpdateFilter = function (user, f) { * Removes a chat filter */ Channel.prototype.removeFilter = function (filter) { - for (var i = 0; i < this.filters.length; i++) { - if (this.filters[i].name === filter.name) { - this.filters.splice(i, 1); + var self = this; + + for (var i = 0; i < self.filters.length; i++) { + if (self.filters[i].name === filter.name) { + self.filters.splice(i, 1); + self.users.forEach(function (u) { + if (self.hasPermission(u, "filteredit")) { + u.socket.emit("deleteChatFilter", filter); + } + }); break; } } diff --git a/lib/chatcommand.js b/lib/chatcommand.js index 117b4365..a1c30c70 100644 --- a/lib/chatcommand.js +++ b/lib/chatcommand.js @@ -72,6 +72,7 @@ var handlers = { /* commands that do not send chat messages */ "afk": function (chan, user, msg, meta) { + console.log("/afk => setAfk(!" + user.meta.afk + ")"); user.setAFK(!user.meta.afk); return true; }, diff --git a/lib/io/ioserver.js b/lib/io/ioserver.js index 6f6e9eec..a0d678e8 100644 --- a/lib/io/ioserver.js +++ b/lib/io/ioserver.js @@ -106,9 +106,12 @@ function handleConnection(sock) { user.emit("login"); user.socket.emit("login", { success: true, - name: user.name + name: user.name, + guest: false }); user.socket.emit("rank", user.global_rank); + } else { + user.socket.emit("rank", -1); } } diff --git a/lib/user.js b/lib/user.js index beedf05a..4018b5b1 100644 --- a/lib/user.js +++ b/lib/user.js @@ -78,6 +78,8 @@ User.prototype.setAFK = function (afk) { return; } + this.meta.afk = afk; + var chan = this.channel; if (afk) { if (chan.voteskip) { @@ -475,7 +477,8 @@ User.prototype.guestLogin = function (name) { self.loggedIn = true; self.socket.emit("login", { success: true, - name: name + name: name, + guest: true }); // TODO you shouldn't be able to guest login without being in a channel diff --git a/templates/channeloptions.jade b/templates/channeloptions.jade index 4ffd77a9..98dea0ff 100644 --- a/templates/channeloptions.jade +++ b/templates/channeloptions.jade @@ -112,6 +112,17 @@ mixin chanranks mixin chatfilters #cs-chatfilters.tab-pane h4 Chat Filters + form.form-horizontal(action="javascript:void(0)", role="form") + +textbox("cs-chatfilters-newname", "Filter name") + +textbox("cs-chatfilters-newregex", "Filter regex") + .form-group + label.control-label.col-sm-4(for="cs-chatfilters-newflags") Flags + .col-sm-8 + input#cs-chatfilters-newflags.form-control.cs-textbox(type="text", value="g") + +textbox("cs-chatfilters-newreplace", "Replacement") + .form-group + .col-sm-8.col-sm-offset-4 + button#cs-chatfilters-newsubmit.btn.btn-primary Create Filter table.table.table-striped.table-condensed thead tr @@ -119,6 +130,10 @@ mixin chatfilters th Name th Active + button#cs-chatfilters-export.btn.btn-default Export filter list + button#cs-chatfilters-import.btn.btn-default Import filter list + textarea#cs-chatfilters-exporttext.form-control(rows="5") + mixin chanlog #cs-chanlog.tab-pane h4 Channel Log diff --git a/templates/useroptions.jade b/templates/useroptions.jade index edca1f5f..74a42704 100644 --- a/templates/useroptions.jade +++ b/templates/useroptions.jade @@ -59,6 +59,17 @@ mixin us-playback mixin rcheckbox("us-hidevideo", "Remove the video player") mixin rcheckbox("us-playlistbuttons", "Hide playlist buttons by default") mixin rcheckbox("us-oldbtns", "Old style playlist buttons") + .form-group + label.control-label.col-sm-4(for="#us-default-quality") Default YouTube quality + .col-sm-8 + select#us-default-quality.form-control + option(value="auto") Auto + option(value="small") 240p + option(value="medium") 360p + option(value="large") 480p + option(value="hd720") 720p + option(value="hd1080") 1080p + option(value="highres") Highest Available mixin us-chat #us-chat.tab-pane diff --git a/www/assets/js/callbacks.js b/www/assets/js/callbacks.js index 8f99d2f3..daf458ee 100644 --- a/www/assets/js/callbacks.js +++ b/www/assets/js/callbacks.js @@ -47,23 +47,17 @@ Callbacks = { socket.emit("joinChannel", { name: CHANNEL.name }); + if (CHANNEL.opts.password) { socket.emit("channelPassword", CHANNEL.opts.password); } - /* - if(NAME && SESSION) { - socket.emit("login", { - name: NAME, - session: SESSION - }); - } - // Guest auto-relogin - else if(CLIENT.name) { + + if (CLIENT.name && CLIENT.guest) { socket.emit("login", { name: CLIENT.name }); } - */ + $("
").addClass("server-msg-reconnect") .text("Connected") .appendTo($("#messagebuffer")); @@ -240,6 +234,44 @@ Callbacks = { formatCSChatFilterList(); }, + updateChatFilter: function (f) { + var entries = $("#cs-chatfilters table").data("entries") || []; + var found = false; + for (var i = 0; i < entries.length; i++) { + if (entries[i].name === f.name) { + entries[i] = f; + found = true; + break; + } + } + + if (!found) { + entries.push(f); + } + + $("#cs-chatfilters table").data("entries", entries); + formatCSChatFilterList(); + }, + + deleteChatFilter: function (f) { + var entries = $("#cs-chatfilters table").data("entries") || []; + var found = false; + for (var i = 0; i < entries.length; i++) { + if (entries[i].name === f.name) { + entries[i] = f; + found = i; + break; + } + } + + if (found !== false) { + entries.splice(found, 1); + } + + $("#cs-chatfilters table").data("entries", entries); + formatCSChatFilterList(); + }, + channelOpts: function(opts) { document.title = opts.pagetitle; PAGETITLE = opts.pagetitle; @@ -429,6 +461,7 @@ Callbacks = { } else { CLIENT.name = data.name; + CLIENT.guest = data.guest; CLIENT.logged_in = true; } }, @@ -478,6 +511,11 @@ Callbacks = { div.data("profile", data.profile); div.data("meta", data.meta); div.data("afk", data.meta.afk); + if (data.meta.muted || data.meta.smuted) { + div.data("icon", "glyphicon-volume-off"); + } else { + div.data("icon", false); + } formatUserlistItem(div, data); addUserDropdown(div, data); div.appendTo($("#userlist")); diff --git a/www/assets/js/data.js b/www/assets/js/data.js index 8ea6bb9f..3e292549 100644 --- a/www/assets/js/data.js +++ b/www/assets/js/data.js @@ -36,7 +36,6 @@ var CHANNEL = { }; var PLAYER = false; -var VIDEOQUALITY = false; var FLUIDLAYOUT = false; var VWIDTH; var VHEIGHT; @@ -84,7 +83,7 @@ function setOpt(k, v) { function getOrDefault(k, def) { var v = getOpt(k); - if(v === null) + if(v === null || v === "null") return def; if(v === "true") return true; @@ -118,7 +117,7 @@ var USEROPTS = { ignore_channeljs : getOrDefault("ignore_channeljs", false), sort_rank : getOrDefault("sort_rank", false), sort_afk : getOrDefault("sort_afk", false), - default_quality : getOrDefault("default_quality", "#quality_auto"), + default_quality : getOrDefault("default_quality", ""), boop : getOrDefault("boop", false), secure_connection : getOrDefault("secure_connection", false) }; diff --git a/www/assets/js/player.js b/www/assets/js/player.js index 63bd2a2a..dabf50b9 100644 --- a/www/assets/js/player.js +++ b/www/assets/js/player.js @@ -67,10 +67,10 @@ var YouTubePlayer = function (data) { self.load = function (data) { if(self.player && self.player.loadVideoById) { self.player.loadVideoById(data.id, data.currentTime); - if(VIDEOQUALITY) { - self.player.setPlaybackQuality(VIDEOQUALITY); + if (USEROPTS.default_quality && USEROPTS.default_quality !== "auto") { + self.player.setPlaybackQuality(USEROPTS.default_quality); // What's that? Another stupid hack for the HTML5 player? - self.player.setPlaybackQuality(VIDEOQUALITY); + self.player.setPlaybackQuality(USEROPTS.default_quality); } self.videoId = data.id; self.videoLength = data.seconds; diff --git a/www/assets/js/ui.js b/www/assets/js/ui.js index aa0776c7..119544a1 100644 --- a/www/assets/js/ui.js +++ b/www/assets/js/ui.js @@ -539,3 +539,73 @@ $("#cs-jssubmit").click(function () { js: $("#cs-jstext").val() }); }); + +$("#cs-chatfilters-newsubmit").click(function () { + var name = $("#cs-chatfilters-newname").val(); + var regex = $("#cs-chatfilters-newregex").val(); + var flags = $("#cs-chatfilters-newflags").val(); + var replace = $("#cs-chatfilters-newreplace").val(); + + try { + new RegExp(regex, flags); + } catch (e) { + alert("Regex error: " + e); + return; + } + + console.log(name, regex, flags, replace); + socket.emit("updateFilter", { + name: name, + source: regex, + flags: flags, + replace: replace, + active: true + }); + + $("#cs-chatfilters-newname").val(""); + $("#cs-chatfilters-newregex").val(""); + $("#cs-chatfilters-newflags").val(""); + $("#cs-chatfilters-newreplace").val(""); +}); + +$("#cs-chatfilters-export").click(function () { + var callback = function (data) { + socket.listeners("chatFilters").splice( + socket.listeners("chatFilters").indexOf(callback) + ); + + $("#cs-chatfilters-exporttext").val(JSON.stringify(data)); + }; + + socket.on("chatFilters", callback); + socket.emit("requestChatFilters"); +}); + +$("#cs-chatfilters-import").click(function () { + var text = $("#cs-chatfilters-exporttext").val(); + var choose = confirm("You are about to import filters from the contents of the textbox below the import button. If this is empty, it will clear all of your filters. Are you sure you want to continue?"); + if (!choose) { + return; + } + + if (text.trim() === "") { + text = "[]"; + } + + var data; + try { + data = JSON.parse(text); + } catch (e) { + alert("Invalid import data: " + e); + return; + } + + var entries = $("#cs-chatfilters table").data("entries") || []; + entries.forEach(function (f) { + socket.emit("removeFilter", f); + }); + + data.forEach(function (f) { + socket.emit("updateFilter", f); + }); +}); diff --git a/www/assets/js/util.js b/www/assets/js/util.js index e9f062cb..b73e61d2 100644 --- a/www/assets/js/util.js +++ b/www/assets/js/util.js @@ -139,7 +139,7 @@ function formatUserlistItem(div) { $("").addClass("glyphicon glyphicon-time").appendTo(icon); } if (data.icon) { - $("").addClass(data.icon).prependTo(icon); + $("").addClass("glyphicon " + data.icon).prependTo(icon); } } @@ -506,7 +506,8 @@ function rebuildPlaylist() { /* user settings menu */ function showUserOptions() { hidePlayer(); - $("#useroptions").on("hidden", function () { + $("#useroptions").on("hidden.bs.modal", function () { + console.log("unhiding"); unhidePlayer(); }); @@ -528,6 +529,7 @@ function showUserOptions() { $("#us-hidevideo").prop("checked", USEROPTS.hidevid); $("#us-playlistbuttons").prop("checked", USEROPTS.qbtn_hide); $("#us-oldbtns").prop("checked", USEROPTS.qbtn_idontlikechange); + $("#us-default-quality").val(USEROPTS.default_quality || "auto"); $("#us-chat-timestamp").prop("checked", USEROPTS.show_timestamps); $("#us-sort-rank").prop("checked", USEROPTS.sort_rank); @@ -556,6 +558,7 @@ function saveUserOptions() { USEROPTS.hidevid = $("#us-hidevideo").prop("checked"); USEROPTS.qbtn_hide = $("#us-playlistbuttons").prop("checked"); USEROPTS.qbtn_idontlikechange = $("#us-oldbtns").prop("checked"); + USEROPTS.default_quality = $("#us-default-quality").val(); USEROPTS.show_timestamps = $("#us-chat-timestamp").prop("checked"); USEROPTS.sort_rank = $("#us-sort-rank").prop("checked"); @@ -839,6 +842,14 @@ function handlePermissionChange() { setVisible("#clearplaylist", hasPermission("playlistclear")); setVisible("#shuffleplaylist", hasPermission("playlistshuffle")); + // Weird things happen to the alignment in chromium when I toggle visibility + // of the above buttons + // This fixes it? + var wtf = $("#rightcontrols .pull-right").removeClass("pull-right") + setTimeout(function () { + wtf.addClass("pull-right"); + }, 1); + setVisible("#newpollbtn", hasPermission("pollctl")); $("#voteskip").attr("disabled", !hasPermission("voteskip") || !CHANNEL.opts.allow_voteskip); @@ -1444,9 +1455,10 @@ function hidePlayer() { return; PLAYER.size = { - width: $("#ytapiplayer").attr("width"), - height: $("#ytapiplayer").attr("height") + width: $("#ytapiplayer").width(), + height: $("#ytapiplayer").height() }; + $("#ytapiplayer").attr("width", 1) .attr("height", 1); } @@ -1458,8 +1470,8 @@ function unhidePlayer() { if(!/(chrome|MSIE)/ig.test(navigator.userAgent)) return; - $("#ytapiplayer").attr("width", PLAYER.size.width) - .attr("height", PLAYER.size.height); + $("#ytapiplayer").width(PLAYER.size.width) + .height(PLAYER.size.height); } function chatDialog(div) { @@ -1654,7 +1666,7 @@ function makeModal() { .html("×") .appendTo(head); - wrap.on("hidden", function () { + wrap.on("hidden.bs.modal", function () { unhidePlayer(); wrap.remove(); }); @@ -1832,7 +1844,6 @@ function formatCSChatFilterList() { $("").addClass("glyphicon glyphicon-trash").appendTo(del); del.click(function () { socket.emit("removeFilter", f); - socket.emit("requestChatFilters"); }); var name = $("").text(f.name).appendTo($("").appendTo(tr)); var activetd = $("").appendTo(tr); @@ -1903,7 +1914,6 @@ function formatCSChatFilterList() { socket.emit("updateFilter", f); reset(); - socket.emit("requestChatFilters"); }); control.data("editor", tr2); diff --git a/www/css/cytube.css b/www/css/cytube.css index f59bcc9c..3ce2fdac 100644 --- a/www/css/cytube.css +++ b/www/css/cytube.css @@ -492,6 +492,10 @@ margin-top: 10px; } -#cs-chatfilters input[type='text'] { +#cs-chatfilters input[type='text'], #cs-chatfilters textarea { font-family: Monospace; } + +#cs-chatfilters-exporttext { + margin-top: 5px; +}