diff --git a/package.json b/package.json
index a54bd028..3715f75a 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"author": "Calvin Montgomery",
"name": "CyTube",
"description": "Online media synchronizer and chat",
- "version": "3.58.3",
+ "version": "3.58.4",
"repository": {
"url": "http://github.com/calzoneman/sync"
},
diff --git a/src/channel/customization.js b/src/channel/customization.js
index 38f644ab..a7db902c 100644
--- a/src/channel/customization.js
+++ b/src/channel/customization.js
@@ -1,5 +1,6 @@
-var ChannelModule = require("./module");
-var XSS = require("../xss");
+const ChannelModule = require("./module");
+const XSS = require("../xss");
+const { hash } = require('../util/hash');
const TYPE_SETCSS = {
css: "string"
@@ -23,6 +24,28 @@ function CustomizationModule(_channel) {
CustomizationModule.prototype = Object.create(ChannelModule.prototype);
+Object.defineProperty(CustomizationModule.prototype, 'css', {
+ get() {
+ return this._css;
+ },
+
+ set(val) {
+ this._css = val;
+ this.cssHash = hash('md5', val, 'base64');
+ }
+});
+
+Object.defineProperty(CustomizationModule.prototype, 'js', {
+ get() {
+ return this._js;
+ },
+
+ set(val) {
+ this._js = val;
+ this.jsHash = hash('md5', val, 'base64');
+ }
+});
+
CustomizationModule.prototype.load = function (data) {
if ("css" in data) {
this.css = data.css;
@@ -69,7 +92,9 @@ CustomizationModule.prototype.onUserPostJoin = function (user) {
CustomizationModule.prototype.sendCSSJS = function (users) {
var data = {
css: this.css,
- js: this.js
+ cssHash: this.cssHash,
+ js: this.js,
+ jsHash: this.jsHash
};
users.forEach(function (u) {
u.socket.emit("channelCSSJS", data);
@@ -89,11 +114,15 @@ CustomizationModule.prototype.handleSetCSS = function (user, data) {
return;
}
- this.dirty = true;
+ let oldHash = this.cssHash;
+ // TODO: consider sending back an error instead of silently truncating
this.css = data.css.substring(0, 20000);
- this.sendCSSJS(this.channel.users);
- this.channel.logger.log("[mod] " + user.getName() + " updated the channel CSS");
+ if (oldHash !== this.cssHash) {
+ this.dirty = true;
+ this.sendCSSJS(this.channel.users);
+ this.channel.logger.log("[mod] " + user.getName() + " updated the channel CSS");
+ }
};
CustomizationModule.prototype.handleSetJS = function (user, data) {
@@ -102,11 +131,14 @@ CustomizationModule.prototype.handleSetJS = function (user, data) {
return;
}
- this.dirty = true;
+ let oldHash = this.jsHash;
this.js = data.js.substring(0, 20000);
- this.sendCSSJS(this.channel.users);
- this.channel.logger.log("[mod] " + user.getName() + " updated the channel JS");
+ if (oldHash !== this.jsHash) {
+ this.dirty = true;
+ this.sendCSSJS(this.channel.users);
+ this.channel.logger.log("[mod] " + user.getName() + " updated the channel JS");
+ }
};
CustomizationModule.prototype.handleSetMotd = function (user, data) {
diff --git a/www/js/callbacks.js b/www/js/callbacks.js
index e8d7d044..21c4c125 100644
--- a/www/js/callbacks.js
+++ b/www/js/callbacks.js
@@ -296,42 +296,54 @@ Callbacks = {
},
channelCSSJS: function(data) {
- $("#chancss").remove();
- CHANNEL.css = data.css;
- $("#cs-csstext").val(data.css);
- if(data.css && !USEROPTS.ignore_channelcss) {
- $("").attr("type", "text/css")
- .attr("id", "chancss")
- .text(data.css)
- .appendTo($("head"));
+ if (CyTube.channelCustomizations.cssHash !== data.cssHash) {
+ $("#chancss").remove();
+ CHANNEL.css = data.css;
+ $("#cs-csstext").val(data.css);
+ if(data.css && !USEROPTS.ignore_channelcss) {
+ $("").attr("type", "text/css")
+ .attr("id", "chancss")
+ .text(data.css)
+ .appendTo($("head"));
+ }
+
+ if (data.cssHash) {
+ CyTube.channelCustomizations.cssHash = data.cssHash;
+ }
}
- $("#chanjs").remove();
- CHANNEL.js = data.js;
- $("#cs-jstext").val(data.js);
+ if (CyTube.channelCustomizations.jsHash !== data.jsHash) {
+ $("#chanjs").remove();
+ CHANNEL.js = data.js;
+ $("#cs-jstext").val(data.js);
- if(data.js && !USEROPTS.ignore_channeljs) {
- var viewSource = document.createElement("button");
- viewSource.className = "btn btn-danger";
- viewSource.textContent = "View inline script source";
- viewSource.onclick = function () {
- var content = document.createElement("pre");
- content.textContent = data.js;
- modalAlert({
- title: "Inline JS",
- htmlContent: content.outerHTML,
- dismissText: "Close"
+ if(data.js && !USEROPTS.ignore_channeljs) {
+ var viewSource = document.createElement("button");
+ viewSource.className = "btn btn-danger";
+ viewSource.textContent = "View inline script source";
+ viewSource.onclick = function () {
+ var content = document.createElement("pre");
+ content.textContent = data.js;
+ modalAlert({
+ title: "Inline JS",
+ htmlContent: content.outerHTML,
+ dismissText: "Close"
+ });
+ };
+
+ checkScriptAccess(viewSource, "embedded", function (pref) {
+ if (pref === "ALLOW") {
+ $("").attr("type", "text/javascript")
+ .attr("id", "chanjs")
+ .text(data.js)
+ .appendTo($("body"));
+ }
});
- };
+ }
- checkScriptAccess(viewSource, "embedded", function (pref) {
- if (pref === "ALLOW") {
- $("").attr("type", "text/javascript")
- .attr("id", "chanjs")
- .text(data.js)
- .appendTo($("body"));
- }
- });
+ if (data.jsHash) {
+ CyTube.channelCustomizations.jsHash = data.jsHash;
+ }
}
},
diff --git a/www/js/data.js b/www/js/data.js
index 793a43d3..b6441b50 100644
--- a/www/js/data.js
+++ b/www/js/data.js
@@ -76,6 +76,10 @@ CyTube.ui = {
CyTube.featureFlag = {
efficientEmotes: true
};
+CyTube.channelCustomizations = {
+ cssHash: null,
+ jsHash: null
+};
CyTube._internal_do_not_use_or_you_will_be_banned = {};
function getOpt(k) {