From 8280b93c97febfdee553fae11b10624ca2a09152 Mon Sep 17 00:00:00 2001
From: SirStendec
Date: Fri, 30 Sep 2016 13:09:03 -0400
Subject: [PATCH] 3.5.301
---
changelog.html | 120 ++++++++
credits.html | 17 ++
dark.css | 73 ++++-
src/ObjectPath.js | 21 ++
src/colors.js | 9 +-
src/commands.js | 205 +++++++++++++-
src/ember/channel.js | 523 +++++++++++++++++++++++++++++++----
src/ember/chat-input.js | 6 +-
src/ember/chatview.js | 97 ++++++-
src/ember/following.js | 77 ++++++
src/ember/layout.js | 66 ++---
src/ember/line.js | 248 ++++++++++++++---
src/ember/moderation-card.js | 393 ++++++++++++++------------
src/ember/room.js | 395 ++++++++++++++++++--------
src/ember/vod-chat.js | 6 +-
src/ext/api.js | 88 +++---
src/ext/betterttv.js | 5 +-
src/main.js | 20 +-
src/settings.js | 4 +-
src/socket.js | 22 +-
src/tokenize.js | 6 +-
src/ui/dark.js | 17 +-
src/ui/following.js | 19 +-
src/ui/menu.js | 65 ++---
src/ui/my_emotes.js | 2 +-
src/ui/races.js | 15 +-
src/utils.js | 84 +++++-
style.css | 140 ++++++++--
28 files changed, 2140 insertions(+), 603 deletions(-)
create mode 100644 credits.html
create mode 100644 src/ObjectPath.js
diff --git a/changelog.html b/changelog.html
index d651336f..3f9cf26e 100644
--- a/changelog.html
+++ b/changelog.html
@@ -1,3 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
" +
- "Example: !permit \"!reg add {user}\" \"/timeout {user} 1 {id}\"
To " +
- "send multiple commands, separate them with <LINE>
.
Numeric values will become timeout buttons for " +
- "that number of seconds. The text <BAN>
is a special value that will act like the normal Ban button in chat.
" +
- "To assign a specific letter for use as the icon, specify it at the start of the command followed by an equals sign.
" +
- "Example: A=\"!reg add\"
Default: <BAN> 600
",
- old_val.substr(1),
+ "Please enter a list of commands to be displayed as moderation buttons within chat lines. " +
+ "One item per line. As a shortcut for specific duration timeouts, you can enter the number of seconds by itself. " +
+ " To send multiple commands, separate them with <LINE>
. " +
+ "Variables, such as the target user's name, can be inserted into your commands. If no variables are detected " +
+ "in a line, {user}
will be added to the end of the first command.
" +
+
+ "To set a custom label for the button, start your line with name:
followed by the " +
+ "name of the button. End the name with an equals sign. Only the first character will be displayed.
" +
+ "Example: name:B=/ban {user}
" +
+
+ "Allowed Variables
" +
+ "{user} | target user's name | " +
+ "{user_name} | target user's name |
" +
+ "{user_display_name} | target user's display name | " +
+ "{user_id} | target user's numeric ID |
" +
+ "{room} | chat room's name | " +
+ "{room_name} | chat room's name |
" +
+ "{room_display_name} | chat room's display name | " +
+ "{room_id} | chat room's numeric ID |
" +
+ "{id} | message's UUID |
" +
+ "
",
+
+ old_val,
function(new_val) {
if ( new_val === null || new_val === undefined )
return;
- var vals = [], prefix = '';
- new_val = new_val.trim();
+ var vals = new_val.trim().split(/\s*\n\s*/g),
+ output = [];
- while(new_val) {
- if ( new_val.charAt(1) === '=' ) {
- prefix = new_val.charAt(0);
- new_val = new_val.substr(2);
- continue;
- }
-
- if ( new_val.charAt(0) === '"' ) {
- var end = new_val.indexOf('"', 1);
- if ( end === -1 )
- end = new_val.length;
-
- var segment = new_val.substr(1, end - 1);
- if ( segment ) {
- vals.push([prefix, segment]);
- prefix = '';
- }
-
- new_val = new_val.substr(end + 1);
-
- } else {
- var ind = new_val.indexOf(' ');
- if ( ind === -1 ) {
- if ( new_val ) {
- vals.push([prefix, new_val]);
- prefix = '';
- }
-
- new_val = '';
-
- } else {
- var segment = new_val.substr(0, ind);
- if ( segment ) {
- vals.push([prefix, segment]);
- prefix = '';
- }
-
- new_val = new_val.substr(ind + 1);
- }
- }
- }
-
- var final = [];
for(var i=0; i < vals.length; i++) {
- var had_prefix = false, prefix = vals[i][0], val = vals[i][1];
- if ( val === "" )
- val = false;
+ var cmd = vals[i],
+ prefix,
+ is_emoji = false,
+ name_match = /^name:([^=]+)=/.exec(cmd);
- var num = parseInt(val);
- if ( num > 0 && ! Number.isNaN(num) )
- val = num;
+ if ( ! cmd || ! cmd.length )
+ continue;
- if ( ! prefix ) {
- var tmp;
- if ( typeof val === "string" )
- tmp = /\w/.exec(val);
- else
- tmp = utils.duration_string(val);
+ if ( name_match ) {
+ label = name_match[1];
- prefix = tmp && tmp.length ? tmp[0].toUpperCase() : "C";
+ if ( window.punycode && punycode.ucs2 )
+ label = punycode.ucs2.encode([punycode.ucs2.decode(label)[0]]);
+
+ // Check for an emoji
+ var tokens = f.tokenize_emoji(label);
+ if ( tokens && tokens[0] && tokens[0].ffzEmoji )
+ is_emoji = tokens[0].ffzEmoji;
+
+ cmd = cmd.substr(name_match[0].length).trim();
} else
- had_prefix = true;
+ label = undefined;
- if ( typeof val === "string" ) {
- // Split it up for this step.
- var lines = val.split(/ * */);
- for(var x=0; x < lines.length; x++) {
- if ( lines[x].indexOf('{user}') === -1 )
- lines[x] += ' {user}';
- }
- val = lines.join("");
+ // Check for a plain ban.
+ if ( /^\/b(?:an)?(?:\s+{user(?:_name)?})?\s*$/.test(cmd) )
+ cmd = false;
+
+ // Numeric Timeout
+ else if ( /^\d+$/.test(cmd) )
+ cmd = parseInt(cmd);
+
+ // Command Timeout
+ else if ( /^\/t(?:imeout)?(?:\s+{user(?:_name)?}(?:\s+(\d+))?)?\s*$/.test(cmd) ) {
+ cmd = parseInt(/^\/t(?:imeout)?(?:\s+{user(?:_name)?}(?:\s+(\d+))?)?\s*$/.exec(cmd)[1]);
+ if ( isNaN(cmd) || ! isFinite(cmd) )
+ cmd = 600;
}
- final.push([prefix, val, had_prefix]);
+
+ // Okay. Do we still need a prefix?
+ if ( label === undefined ) {
+ var tmp;
+ if ( typeof cmd === "string" )
+ tmp = /\w/.exec(cmd);
+ else
+ tmp = utils.duration_string(cmd);
+
+ label = tmp && tmp.length ? tmp[0].toUpperCase() : 'C';
+ }
+
+ // Add {user} to the first command if it's a custom command and missing.
+ if ( typeof cmd === "string" ) {
+ utils.CMD_VAR_REGEX.lastIndex = 0;
+ if ( ! utils.CMD_VAR_REGEX.test(cmd) ) {
+ var lines = cmd.split(/\s*\s*/g);
+ lines[0] += ' {user}';
+ cmd = lines.join("");
+ }
+ }
+
+ output.push([label, cmd, name_match != null, is_emoji]);
}
- f.settings.set('mod_buttons', final);
+ f.settings.set('mod_buttons', output);
// Update existing chat lines.
var CL = utils.ember_resolve('component:chat/chat-line'),
@@ -455,7 +477,7 @@ FFZ.settings_info.mod_buttons = {
view.$('.mod-icons').replaceWith(view.buildModIconsHTML());
}
- }, 600);
+ }, 600, input);
}
};
@@ -472,60 +494,74 @@ FFZ.settings_info.mod_card_buttons = {
method: function() {
var f = this,
- old_val = "";
+ old_val = "",
+ input = utils.createElement('textarea');
+
+ input.style.marginBottom = '20px';
+
for(var i=0; i < this.settings.mod_card_buttons.length; i++) {
- var cmd = this.settings.mod_card_buttons[i];
- if ( cmd.indexOf(' ') !== -1 )
- old_val += ' "' + cmd + '"';
- else
- old_val += ' ' + cmd;
+ var label, cmd, had_label, pair = this.settings.mod_card_buttons[i];
+ if ( Array.isArray(pair) ) {
+ label = pair[0];
+ cmd = pair[1];
+ had_label = pair[2];
+ } else {
+ cmd = pair;
+ had_label = false;
+ }
+
+ label = had_label ? 'name:' + label + '=' : '';
+ old_val += (old_val.length ? '\n' : '') + label + cmd;
}
utils.prompt(
"Moderation Card Additional Buttons",
- "Please enter a list of additional commands to display buttons for on moderation cards. Commands are separated by spaces. " +
- "To include spaces in a command, surround the command with double quotes (\"). Use {user}
to insert the " +
- "user's name into the command, otherwise it will be appended to the end.Example: !permit \"!reg add {user}\"",
- old_val.substr(1),
+ "Please enter a list of additional commands to display buttons for on moderation cards. " +
+ "One item per line. To send multiple commands, separate them with <LINE>
. " +
+ "Variables, such as the target user's name, can be inserted into your commands. If no variables are detected " +
+ "in a line, {user}
will be added to the end of the first command.
" +
+
+ "To set a custom label for the button, start your line with name:
followed by the name of the button. " +
+ "End the name with an equals sign.
" +
+ "Example: name:Boop=/timeout {user} 15 Boop!
" +
+
+ "Allowed Variables
" +
+ "{user} | target user's name | " +
+ "{user_name} | target user's name |
" +
+ "{user_display_name} | target user's display name | " +
+ "{user_id} | target user's numeric ID |
" +
+ "{room} | chat room's name | " +
+ "{room_name} | chat room's name |
" +
+ "{room_display_name} | chat room's display name | " +
+ "{room_id} | chat room's numeric ID |
" +
+ "
",
+ old_val,
function(new_val) {
if ( new_val === null || new_val === undefined )
return;
- var vals = [];
- new_val = new_val.trim();
+ var vals = new_val.trim().split(/\s*\n\s*/g),
+ output = [];
- while(new_val) {
- if ( new_val.charAt(0) === '"' ) {
- var end = new_val.indexOf('"', 1);
- if ( end === -1 )
- end = new_val.length;
+ for(var i=0; i < vals.length; i++) {
+ var cmd = vals[i],
+ label,
+ name_match = /^name:([^=]+)=/.exec(cmd);
- var segment = new_val.substr(1, end - 1);
- if ( segment )
- vals.push(segment);
+ if ( ! cmd || ! cmd.length )
+ continue;
- new_val = new_val.substr(end + 1);
+ if ( name_match ) {
+ label = name_match[1];
+ cmd = cmd.substr(name_match[0].length);
+ } else
+ label = cmd.split(' ', 1)[0]
- } else {
- var ind = new_val.indexOf(' ');
- if ( ind === -1 ) {
- if ( new_val )
- vals.push(new_val);
-
- new_val = '';
-
- } else {
- var segment = new_val.substr(0, ind);
- if ( segment )
- vals.push(segment);
-
- new_val = new_val.substr(ind + 1);
- }
- }
+ output.push([label, cmd, name_match != null]);
}
- f.settings.set("mod_card_buttons", vals);
- }, 600);
+ f.settings.set("mod_card_buttons", output);
+ }, 600, input);
}
};
@@ -586,11 +622,11 @@ FFZ.prototype.setup_mod_card = function() {
this.log("Listening to the Settings controller to catch mod icon state changes.");
var f = this,
- Settings = utils.ember_lookup('controller:settings');
+ Settings = utils.ember_settings();
if ( Settings )
- Settings.addObserver('settings.showModIcons', function() {
- if ( Settings.get('settings.showModIcons') )
+ Settings.addObserver('showModIcons', function() {
+ if ( Settings.get('showModIcons') )
f.settings.set('chat_mod_icon_visibility', 1);
});
@@ -684,7 +720,8 @@ FFZ.prototype.modify_moderation_card = function(component) {
chat = utils.ember_lookup('controller:chat'),
user = f.get_user(),
- room_id = chat && chat.get('currentRoom.id'),
+ room = chat && chat.get('currentRoom'),
+ room_id = room && room.get('id'),
is_broadcaster = user && room_id === user.login,
user_id = controller.get('cardInfo.user.id'),
@@ -747,13 +784,12 @@ FFZ.prototype.modify_moderation_card = function(component) {
if ( is_mod && f.settings.mod_card_buttons && f.settings.mod_card_buttons.length ) {
line = utils.createElement('div', 'extra-interface interface clearfix');
- var cmds = {},
- add_btn_click = function(cmd) {
- var user_id = controller.get('cardInfo.user.id'),
- cont = utils.ember_lookup('controller:chat'),
- room = cont && cont.get('currentRoom'),
+ var add_btn_click = function(cmd) {
+ var user = controller.get('cardInfo.user'),
+ chat_controller = utils.ember_lookup('controller:chat'),
+ room = chat_controller && chat_controller.get('currentRoom'),
- cm = cmd.replace(USER_REG, user_id),
+ cm = utils.replace_cmd_variables(cmd, user, room),
reason = ban_reason();
if ( reason ) {
@@ -775,36 +811,49 @@ FFZ.prototype.modify_moderation_card = function(component) {
room && room.send(cm, true);
},
- add_btn_make = function(cmd) {
- var btn = utils.createElement('button', 'button ffz-no-bg'),
- segment = cmd.split(' ', 1)[0],
- title = cmds[segment] > 1 ? cmd.split(' ', cmds[segment]) : [segment];
+ add_btn_make = function(label, cmd) {
+ var btn = utils.createElement('button', 'button ffz-no-bg', utils.sanitize(label));
- if ( /^[!~./]/.test(title[0]) )
- title[0] = title[0].substr(1);
+ jQuery(btn).tipsy({
+ html: true,
+ gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'n'),
+ title: function() {
+ var user = controller.get('cardInfo.user'),
+ chat_controller = utils.ember_lookup('controller:chat'),
+ room = chat_controller && chat_controller.get('currentRoom');
- title = _.map(title, function(s){ return s.capitalize() }).join(' ');
+ title = utils.replace_cmd_variables(cmd, user, room);
- btn.innerHTML = utils.sanitize(title);
- btn.title = utils.sanitize(cmd.replace(/{user}/g, controller.get('cardInfo.user.id') || '{user}'));
+ title = _.map(title.split(/\s*\s*/g, utils.sanitize).join("
"));
+
+ return "Custom Command" + (title.indexOf('
') !== -1 ? 's' : '') +
+ "
" + title;
+ }
+ });
- jQuery(btn).tipsy({gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'n')});
btn.addEventListener('click', add_btn_click.bind(this, cmd));
return btn;
};
- var cmds = {};
- for(var i=0; i < f.settings.mod_card_buttons.length; i++)
- cmds[f.settings.mod_card_buttons[i].split(' ',1)[0]] = (cmds[f.settings.mod_card_buttons[i].split(' ',1)[0]] || 0) + 1;
for(var i=0; i < f.settings.mod_card_buttons.length; i++) {
- var cmd = f.settings.mod_card_buttons[i],
- ind = cmd.indexOf('{user}');
+ var label, cmd, pair = f.settings.mod_card_buttons[i];
+ if ( ! Array.isArray(pair) ) {
+ cmd = pair;
+ label = cmd.split(' ', 1)[0];
+ } else {
+ label = pair[0];
+ cmd = pair[1];
+ }
- if ( ind === -1 )
- cmd += ' {user}';
+ utils.CMD_VAR_REGEX.lastIndex = 0;
+ if ( ! utils.CMD_VAR_REGEX.test(cmd) ) {
+ var lines = cmd.split(/\s*\s*/g);
+ lines[0] += ' {user}';
+ cmd = lines.join("");
+ }
- line.appendChild(add_btn_make(cmd))
+ line.appendChild(add_btn_make(label, cmd));
}
el.appendChild(line);
@@ -1094,7 +1143,7 @@ FFZ.prototype.modify_moderation_card = function(component) {
}
if ( user_history.length < 50 ) {
- var before = (user_history.length > 0 ? user_history[0].date.getTime() : Date.now()) - (f._ws_server_offset || 0);
+ var before = (user_history.length > 0 && user_history[0].date ? user_history[0].date.getTime() : Date.now()) - (f._ws_server_offset || 0);
f.ws_send("user_history", [room_id, user_id, 50 - user_history.length], function(success, data) {
if ( ! success )
return;
@@ -1271,9 +1320,9 @@ FFZ.prototype._build_mod_card_history = function(msg, modcard, show_from) {
colors = raw_color && this._handle_color(raw_color),
Layout = utils.ember_lookup('service:layout'),
- Settings = utils.ember_lookup('controller:settings'),
+ Settings = utils.ember_settings(),
- is_dark = (Layout && Layout.get('isTheatreMode')) || (Settings && Settings.get('settings.darkMode'));
+ is_dark = (Layout && Layout.get('isTheatreMode')) || this.settings.get_twitch("darkMode");
// Styling
diff --git a/src/ember/room.js b/src/ember/room.js
index 3cbf0f71..5ecd409e 100644
--- a/src/ember/room.js
+++ b/src/ember/room.js
@@ -12,17 +12,28 @@ var FFZ = window.FrankerFaceZ,
'subscribers': 'subs_on',
'subscribersoff': 'subs_off',
'emoteonly': 'emote_only_on',
- 'emoteonlyoff': 'emote_only_off'
+ 'emoteonlyoff': 'emote_only_off',
+ 'host': 'host_on',
+ 'unhost': 'host_off'
},
STATUS_BADGES = [
["r9k", "r9k", "This room is in R9K-mode."],
["emote", "emoteOnly", "This room is in Twitch emoticons only mode. Emoticons added by extensions are not available in this mode."],
["sub", "subsOnly", "This room is in subscribers-only mode."],
- ["slow", "slow", function(room) { return "This room is in slow mode. You may send messages every " + utils.number_commas(room && room.get('slow') || 120) + " seconds." }],
+ ["slow", "slow", function(room) { return "This room is in slow mode. You may send messages every " + utils.number_commas(room && room.get('slow') || 120) + " seconds." }],
["ban", "ffz_banned", "You have been banned from talking in this room."],
- ["delay", function(room) { return room && room.get('ffz_chat_delay') !== 0 }, function(room) { return "Artificial chat delay is enabled. Messages are displayed after " + (room ? room.get('ffz_chat_delay')/1000 : 0) + " seconds." }],
- ["batch", function() { return this.settings.chat_batching !== 0 }, function() { return "You have enabled chat message batching. Messages are displayed in " + (this.settings.chat_batching/1000) + " second increments." }]
+ ["delay", function(room) {
+ return room && (this.settings.chat_delay === -1 ?
+ room.get('roomProperties.chat_delay_duration')
+ : room.get('ffz_chat_delay'))
+ }, function(room) {
+ var is_mod = this.settings.chat_delay === -1;
+ return "Artificial chat delay is enabled" + (is_mod ? " for this channel" : "") + ". Messages are displayed after " + (room ? (is_mod ? room.get('roomProperties.chat_delay_duration') : room.get('ffz_chat_delay')/1000) : 0) + " seconds" + (is_mod ? " for non-moderators." : ".");
+ }, null, function(room) {
+ return room && this.settings.chat_delay === -1 && room.get('isModeratorOrHigher') || false;
+ }],
+ ["batch", function() { return this.settings.chat_batching !== 0 }, function() { return "You have enabled chat message batching. Messages are displayed in " + (this.settings.chat_batching/1000) + " second increments." }]
],
// StrimBagZ Support
@@ -32,7 +43,7 @@ var FFZ = window.FrankerFaceZ,
if ( ! room.moderator_badge )
return "";
- return '.chat-line[data-room="' + room.id + '"] .badges .moderator:not(.ffz-badge-replacement) { background-repeat: no-repeat; background-size: initial; background-position: center; background-image:url("' + room.moderator_badge + '") !important; }';
+ return '.from-display-preview[data-room="' + room.id + '"] .badges .moderator:not(.ffz-badge-replacement),.chat-line[data-room="' + room.id + '"] .badges .moderator:not(.ffz-badge-replacement) { background-repeat: no-repeat; background-size: initial; background-position: center; background-image:url("' + room.moderator_badge + '") !important; }';
};
@@ -156,14 +167,14 @@ FFZ.prototype._modify_chat_pubsub = function(pubsub) {
token = user.chat_oauth_token,
new_topics = [
"chat_message_updated." + room_id,
- "chat_moderator_actions." + room_id];
+ "chat_moderator_actions." + user.id + "." + room_id];
for(var i=0; i < new_topics.length; i++)
ps.Listen({
topic: new_topics[i],
auth: token,
success: function() {},
- failure: function() {},
+ failure: function(t) { f.log("[PubSub] Failed to listen to topic: " + new_topics[i], t); },
message: Ember.run.bind(n, n._onPubsubMessage, new_topics[i])
});
@@ -199,7 +210,7 @@ FFZ.prototype._modify_chat_pubsub = function(pubsub) {
ps.Unlisten({
topic: old_topics[i],
success: function() {},
- failure: function() {}
+ failure: function(t) { f.log("[PubSub] Failed to unlisten to topic: " + old_topics[i], t); }
});
this.chatTopics.removeObject(old_topics[i]);
}
@@ -238,14 +249,14 @@ FFZ.prototype._modify_chat_pubsub = function(pubsub) {
ps.Unlisten({
topic: pubsub.chatTopics[i],
success: function() {},
- failure: function() {}
+ failure: function(t) { f.log("[PubSub] Failed to unlisten to topic: " + old_topics[i], t); }
});
ps.Listen({
topic: pubsub.chatTopics[i],
auth: token,
success: function() {},
- failure: function() {},
+ failure: function(t) { f.log("[PubSub] Failed to listen to topic: " + new_topics[i], t); },
message: Ember.run.bind(pubsub, pubsub._onPubsubMessage, pubsub.chatTopics[i])
});
}
@@ -424,7 +435,7 @@ FFZ.prototype.modify_room_view = function(view) {
if ( ! badge ) {
badge = utils.createElement('span', 'ffz room-state stat float-right', (label || info[0]).charAt(0).toUpperCase() + '' + (label || info[0]).substr(1).toUpperCase() + '');
badge.id = id;
- jQuery(badge).tipsy({gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'se')});
+ jQuery(badge).tipsy({html: true, gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'se')});
cont.appendChild(badge);
}
@@ -433,6 +444,7 @@ FFZ.prototype.modify_room_view = function(view) {
badge.title = typeof info[2] === "function" ? info[2].call(f, room) : info[2];
badge.classList.toggle('hidden', ! visible);
+ badge.classList.toggle('faded', info[4] !== undefined ? typeof info[4] === "function" ? info[4].call(f, room) : info[4] : false);
if ( visible )
vis_count++;
}
@@ -576,7 +588,8 @@ FFZ.prototype.modify_room_view = function(view) {
var e = this,
s = this._$chatMessagesScroller;
- Ember.run.next(function() {
+ //this.runTask(function() {
+ Ember.run.next(function(){
// Trying random performance tweaks for fun and profit!
(window.requestAnimationFrame||setTimeout)(function(){
if ( e.ffz_frozen || ! s || ! s.length )
@@ -584,13 +597,16 @@ FFZ.prototype.modify_room_view = function(view) {
s[0].scrollTop = s[0].scrollHeight;
e._setStuckToBottom(true);
+ e._tagVisibleMessages();
})
})
}, 200),
_setStuckToBottom: function(val) {
this.set("stuckToBottom", val);
- this.get("controller.model") && this.set("controller.model.messageBufferSize", f.settings.scrollback_length + (val ? 0 : 150));
+ var model = this.get("controller.model");
+ if ( model )
+ model.messageBufferSize = f.settings.scrollback_length + (val ? 0 : 150);
if ( ! val )
this.ffzUnfreeze(true);
},
@@ -649,15 +665,13 @@ FFZ.ffz_commands = {};
FFZ.prototype.room_message = function(room, text) {
- var lines = text.split("\n");
if ( this.has_bttv ) {
+ var lines = text.split("\n");
for(var i=0; i < lines.length; i++)
BetterTTV.chat.handlers.onPrivmsg(room.id, {style: 'admin', date: new Date(), from: 'jtv', message: lines[i]});
- } else {
- for(var i=0; i < lines.length; i++)
- room.room.addMessage({style: 'ffz admin', date: new Date(), from: 'FFZ', message: lines[i]});
- }
+ } else
+ room.room.addMessage({ffz_line_returns: true, style: 'ffz admin', date: new Date(), from: 'FFZ', message: text});
}
@@ -821,10 +835,10 @@ FFZ.prototype.add_room = function(id, room) {
this.ws_sub("room." + id);
// Do we want history?
- if ( ! this.has_bttv && this.settings.chat_history && room && (room.get('messages.length') || 0) < 10 ) {
+ /*if ( ! this.has_bttv && this.settings.chat_history && room && (room.get('messages.length') || 0) < 10 ) {
if ( ! this.ws_send("chat_history", [id,25], this._load_history.bind(this, id)) )
data.needs_history = true;
- }
+ }*/
}
@@ -874,7 +888,7 @@ FFZ.prototype.remove_room = function(id) {
// Chat History
// --------------------
-FFZ.prototype._load_history = function(room_id, success, data) {
+/*FFZ.prototype._load_history = function(room_id, success, data) {
var room = this.rooms[room_id];
if ( ! room || ! room.room )
return;
@@ -888,7 +902,7 @@ FFZ.prototype._load_history = function(room_id, success, data) {
return;
return this._insert_history(room_id, data, true);
-}
+}*/
FFZ.prototype._show_deleted = function(room_id) {
@@ -1224,6 +1238,9 @@ FFZ.prototype._modify_room = function(room) {
if ( event.topic && event.topic.substr(-room_id.length) !== room_id || event.created_by === this.get("session.userData.login") )
return;
+ if ( ! f.settings.get_twitch('showModerationActions') )
+ return;
+
var target_notice = NOTICE_MAPPING[event.moderation_action];
if ( target_notice ) {
var last_notice = this.ffz_last_notices && this.ffz_last_notices[target_notice];
@@ -1233,13 +1250,13 @@ FFZ.prototype._modify_room = function(room) {
last_notice.has_owner = true;
last_notice.cachedTokens = undefined;
if ( last_notice._line )
- last_notice._line.ffzRender();
+ Ember.propertyDidChange(last_notice._line, 'ffzTokenizedMessage');
} else {
var waiting = this.ffz_waiting_notices = this.ffz_waiting_notices || {};
waiting[target_notice] = event.created_by;
}
- } else if ( f.settings.get_twitch('showModerationActions') )
+ } else
this._super(event);
},
@@ -1249,6 +1266,8 @@ FFZ.prototype._modify_room = function(room) {
if ( event.topic && event.topic.substr(-room_id.length) !== room_id || event.created_by === this.get("session.userData.login") )
return;
+ // f.log("Login Moderation", event);
+
// In case we get unexpected input, do the other thing.
if ( ["ban", "unban", "timeout"].indexOf(event.moderation_action) === -1 )
return this._super(event);
@@ -1259,7 +1278,7 @@ FFZ.prototype._modify_room = function(room) {
'ban-moderator': event.created_by
};
- this.clearMessages(event.args[0], tags, false, event.moderation_action !== 'unban');
+ this.clearMessages(event.args[0].toLowerCase(), tags, false, event.moderation_action !== 'unban');
},
clearMessages: function(user, tags, disable_log, report_only) {
@@ -1424,6 +1443,7 @@ FFZ.prototype._modify_room = function(room) {
// Look up the user's last ban.
var show_notice = is_me || this.ffzShouldDisplayNotice(),
show_reason = is_me || this.get('isModeratorOrHigher'),
+ show_moderator = f.settings.get_twitch('showModerationActions'),
room = f.rooms && f.rooms[t.get('id')],
now = new Date,
end_time = now + (duration * 1000),
@@ -1457,7 +1477,7 @@ FFZ.prototype._modify_room = function(room) {
durations: [duration],
end_time: end_time,
timeouts: report_only ? 0 : 1,
- message: message + (show_reason && moderator ? ' by ' + moderator : '') + (show_reason && reason ? ' with reason: ' + reason : '.')
+ message: message + (show_reason && show_moderator && moderator ? ' by ' + moderator : '') + (show_reason && reason ? ' with reason: ' + reason : '.')
};
if ( ban_history )
@@ -1485,7 +1505,7 @@ FFZ.prototype._modify_room = function(room) {
last_ban.message = message +
(last_ban.timeouts > 1 ? ' (' + utils.number_commas(last_ban.timeouts) + ' times)' : '') +
- (!show_reason || last_ban.moderators.length === 0 ? '' : ' by ' + last_ban.moderators.join(', ') ) +
+ (!show_reason || !show_moderator || last_ban.moderators.length === 0 ? '' : ' by ' + last_ban.moderators.join(', ') ) +
(!show_reason || last_ban.reasons.length === 0 ? '.' : ' with reason' + utils.pluralize(last_ban.reasons.length) + ': ' + last_ban.reasons.join(', '));
last_ban.cachedTokens = [{type: "text", text: last_ban.message}];
@@ -1556,7 +1576,7 @@ FFZ.prototype._modify_room = function(room) {
}
},
- trimMessages: function() {
+ /*trimMessages: function() {
var messages = this.get("messages"),
len = messages.get("length"),
limit = this.get("messageBufferSize");
@@ -1578,7 +1598,7 @@ FFZ.prototype._modify_room = function(room) {
messages.removeAt(0, to_remove);
}
- },
+ },*/
// Artificial chat delay
ffz_chat_delay: function() {
@@ -1608,14 +1628,81 @@ FFZ.prototype._modify_room = function(room) {
this.ffzSchedulePendingFlush(now);
} else {
- this.ffzActualPushMessage(msg);
+ this.ffzPushMessages([msg]);
}
},
- ffzActualPushMessage: function (msg) {
+ ffzPushMessages: function(messages) {
+ var new_messages = [],
+ new_unread = 0;
+
+ for(var i=0; i < messages.length; i++) {
+ var msg = messages[i];
+ if ( this.shouldShowMessage(msg) && this.ffzShouldShowMessage(msg) ) {
+ new_messages.push(msg);
+
+ if ( ! (msg.tags && msg.tags.historical) && msg.style !== "admin" && msg.style !== "whisper" ) {
+ if ( msg.ffz_has_mention )
+ this.ffz_last_mention = Date.now();
+
+ new_unread++;
+ }
+ }
+ }
+
+ if ( ! new_messages.length )
+ return;
+
+ var room_messages = this.get("messages"),
+ trimmed = room_messages.length + new_messages.length > this.messageBufferSize ?
+ room_messages.slice(Math.max(0, (room_messages.length - this.messageBufferSize)) + new_messages.length, room_messages.length) :
+ room_messages.slice(0, room_messages.length);
+
+ var earliest_message;
+ for(var i=0; i < trimmed.length; i++)
+ if ( trimmed[i].date ) {
+ earliest_message = trimmed[i].date;
+ break;
+ }
+
+ for(var i = 0; i < new_messages.length; i++) {
+ var msg = new_messages[i];
+ if ( msg.tags && msg.tags.historical ) {
+ // Add a warning about really old messages.
+ if ( earliest_message ) {
+ var age = earliest_message - msg.date;
+ if ( age > 300000 )
+ trimmed.unshift({
+ color: '#755000',
+ date: msg.date,
+ from: 'frankerfacez_admin',
+ style: 'admin',
+ message: '(Last message is ' + utils.human_time(age/1000) + ' old.)',
+ room: msg.room,
+ from_server: true
+ });
+ }
+
+ trimmed.unshift(msg);
+ earliest_message = null;
+
+ } else
+ trimmed.push(msg);
+ }
+
+ this.set("messages", trimmed);
+
+ if ( new_unread ) {
+ this.incrementProperty("unreadCount", new_unread);
+ this.ffz_last_activity = Date.now();
+ }
+ },
+
+ /*ffzActualPushMessage: function (msg) {
if ( this.shouldShowMessage(msg) && this.ffzShouldShowMessage(msg) ) {
this.get("messages").pushObject(msg);
this.trimMessages();
+ Ember.propertyDidChange(this, "messages");
if ( msg.style !== "admin" && msg.style !== "whisper" ) {
if ( msg.ffz_has_mention ) {
@@ -1626,7 +1713,7 @@ FFZ.prototype._modify_room = function(room) {
this.incrementProperty("unreadCount", 1);
}
}
- },
+ },*/
ffzSchedulePendingFlush: function(now) {
// Instead of just blindly looping every x seconds, we want to calculate the time until
@@ -1655,7 +1742,8 @@ FFZ.prototype._modify_room = function(room) {
this._ffz_pending_flush = null;
var now = this._ffz_last_batch = Date.now(),
- chat_delay = this.get('ffz_chat_delay');
+ chat_delay = this.get('ffz_chat_delay'),
+ to_display = [];
for (var i = 0, l = this.ffzPending.length; i < l; i++) {
var msg = this.ffzPending[i];
@@ -1677,9 +1765,11 @@ FFZ.prototype._modify_room = function(room) {
break;
msg.pending = false;
- this.ffzActualPushMessage(msg);
+ to_display.push(msg);
}
+ this.ffzPushMessages(to_display);
+
this.ffzPending = this.ffzPending.slice(i);
this.ffzSchedulePendingFlush(now);
},
@@ -1714,7 +1804,7 @@ FFZ.prototype._modify_room = function(room) {
if ( (msg.msgId === 'timeout_success' || msg.msgId === 'ban_success') && this.ffzShouldDisplayNotice() )
return;
- f.log("Notification", msg);
+ // f.log("Notification", msg);
if ( ! msg.tags )
msg.tags = {};
@@ -1740,7 +1830,7 @@ FFZ.prototype._modify_room = function(room) {
this.addMessage(msg);
},
- addMessage: function(msg) {
+ ffzProcessMessage: function(msg) {
if ( msg ) {
var notice_type = msg.tags && msg.tags['msg-id'],
is_resub = notice_type === 'resub',
@@ -1769,6 +1859,13 @@ FFZ.prototype._modify_room = function(room) {
msg.tags['system-msg'] = '';
}
+ // Fix dates for historical messages.
+ if ( ! msg.date && msg.tags && msg.tags['tmi-sent-ts'] ) {
+ var sent = parseInt(msg.tags['tmi-sent-ts']);
+ if ( sent && ! isNaN(sent) && isFinite(sent) )
+ msg.date = new Date(sent);
+ }
+
var is_whisper = msg.style === 'whisper';
// Ignore whispers if conversations are enabled.
@@ -1792,7 +1889,7 @@ FFZ.prototype._modify_room = function(room) {
// Tokenization
f.tokenize_chat_line(msg, false, this.get('roomProperties.hide_chat_links'));
- // If it's from Twitch notify, and it's directly related to
+ // Check for a new subscription line that would need a chat badge.
if ( msg.from === 'twitchnotify' && msg.message.indexOf('subscribed to') === -1 && msg.message.indexOf('subscribed') !== -1 ) {
if ( ! msg.tags )
msg.tags = {};
@@ -1804,26 +1901,55 @@ FFZ.prototype._modify_room = function(room) {
msg.labels.push("subscriber");
}
- // Keep the history.
- if ( ! is_whisper && msg.from && msg.from !== 'jtv' && msg.from !== 'twitchnotify' && f.settings.mod_card_history ) {
- var room = f.rooms && f.rooms[msg.room];
- if ( room ) {
- var chat_history = room.user_history = room.user_history || {},
- user_history = room.user_history[msg.from] = room.user_history[msg.from] || [],
- last_history = user_history.length && user_history[user_history.length - 1],
+ // Color processing.
+ if ( msg.color )
+ f._handle_color(msg.color);
- new_msg = {
- from: msg.from,
- tags: {
- 'display-name': msg.tags && msg.tags['display-name'],
- bits: msg.tags && msg.tags.bits
- },
- message: msg.message,
- cachedTokens: msg.cachedTokens,
- style: msg.style,
- date: msg.date
- };
+ return msg;
+ }
+ },
+ addMessage: function(msg) {
+ msg = this.ffzProcessMessage(msg);
+ if ( ! msg )
+ return;
+
+ var msg_id = msg.tags && msg.tags.id,
+ notice_type = msg.tags && msg.tags['msg-id'],
+ is_whisper = msg.style === 'whisper';
+
+
+ // If this message is already in the room, discard the duplicate.
+ if ( msg_id && this.ffz_ids && this.ffz_ids[msg_id] )
+ return;
+
+
+ // Keep the history.
+ if ( ! is_whisper && msg.from && msg.from !== 'jtv' && msg.from !== 'twitchnotify' && f.settings.mod_card_history ) {
+ var room = f.rooms && f.rooms[msg.room];
+ if ( room ) {
+ var chat_history = room.user_history = room.user_history || {},
+ user_history = room.user_history[msg.from] = room.user_history[msg.from] || [],
+ last_history = user_history.length && user_history[user_history.length - 1],
+
+ new_msg = {
+ from: msg.from,
+ tags: {
+ 'display-name': msg.tags && msg.tags['display-name'],
+ bits: msg.tags && msg.tags.bits
+ },
+ message: msg.message,
+ cachedTokens: msg.cachedTokens,
+ style: msg.style,
+ date: msg.date
+ };
+
+ if ( msg.tags && msg.tags.historical ) {
+ // If it's historical, insert it at the beginning. And stuff.
+ if ( user_history.length < 20 )
+ user_history.unshift(new_msg);
+
+ } else {
// Preserve message order if we *just* received a ban.
if ( last_history && last_history.is_delete && (msg.date - last_history.date) <= 200 ) {
user_history.splice(user_history.length - 1, 0, new_msg);
@@ -1832,63 +1958,60 @@ FFZ.prototype._modify_room = function(room) {
if ( user_history.length > 20 )
user_history.shift();
+ }
- if ( f._mod_card && f._mod_card.ffz_room_id === msg.room && f._mod_card.get('cardInfo.user.id') === msg.from ) {
- var el = f._mod_card.get('element'),
- history = el && el.querySelector('.chat-history:not(.adjacent-history)'),
- was_at_top = history && history.scrollTop >= (history.scrollHeight - history.clientHeight);
+ if ( f._mod_card && f._mod_card.ffz_room_id === msg.room && f._mod_card.get('cardInfo.user.id') === msg.from ) {
+ var el = f._mod_card.get('element'),
+ history = el && el.querySelector('.chat-history:not(.adjacent-history)'),
+ was_at_top = history && history.scrollTop >= (history.scrollHeight - history.clientHeight);
- if ( history ) {
- history.appendChild(f._build_mod_card_history(msg, f._mod_card));
- if ( was_at_top )
- setTimeout(function() { history.scrollTop = history.scrollHeight; })
+ if ( history ) {
+ var el = f._build_mod_card_history(msg, f._mod_card);
+ if ( msg.tags && msg.tags.historical )
+ history.insertBefore(el, history.firstElementChild);
+ else
+ history.appendChild(el);
+ if ( was_at_top )
+ setTimeout(function() { history.scrollTop = history.scrollHeight; })
- // Don't do infinite scrollback.
- if ( history.childElementCount > 100 )
- history.removeChild(history.firstElementChild);
- }
+ // Don't do infinite scrollback.
+ if ( history.childElementCount > 100 )
+ history.removeChild(history.firstElementChild);
}
}
}
-
- // Clear the last ban for that user.
- var f_room = f.rooms && f.rooms[msg.room],
- ban_history = f_room && f_room.ban_history;
-
- if ( ban_history && msg.from ) {
- // Is the last ban within 200ms? Chances are Twitch screwed up message order.
- if ( ban_history[msg.from] && (new Date - ban_history[msg.from].date) <= 200 ) {
- msg.ffz_deleted = true;
- msg.deleted = !f.settings.prevent_clear;
-
- } else
- ban_history[msg.from] = false;
- }
-
-
- // Check for message from us.
- if ( ! is_whisper && ! msg.ffz_deleted ) {
- var user = f.get_user();
- if ( user && user.login === msg.from ) {
- var was_banned = this.get('ffz_banned');
- this.set('ffz_banned', false);
-
- // Update the wait time.
- if ( this.get('isSubscriber') || this.get('isModeratorOrHigher') || ! this.get('slowMode') )
- this.updateWait(0, was_banned)
- else if ( this.get('slowMode') )
- this.updateWait(this.get('slow'));
- }
- }
-
- // Also update chatters.
- if ( ! is_whisper && this.chatters && ! this.chatters[msg.from] && msg.from !== 'twitchnotify' && msg.from !== 'jtv' )
- this.ffzUpdateChatters(msg.from);
}
- // Color processing.
- if ( msg.color )
- f._handle_color(msg.color);
+ // Clear the last ban for that user.
+ var f_room = f.rooms && f.rooms[msg.room],
+ ban_history = f_room && f_room.ban_history;
+
+ if ( ban_history && msg.from ) {
+ // Is the last ban within 200ms? Chances are Twitch screwed up message order.
+ if ( ban_history[msg.from] && (new Date - ban_history[msg.from].date) <= 200 ) {
+ msg.ffz_deleted = true;
+ msg.deleted = !f.settings.prevent_clear;
+
+ } else
+ ban_history[msg.from] = false;
+ }
+
+
+ // Check for message from us.
+ if ( ! is_whisper && ! msg.ffz_deleted ) {
+ var user = f.get_user();
+ if ( user && user.login === msg.from ) {
+ var was_banned = this.get('ffz_banned');
+ this.set('ffz_banned', false);
+
+ // Update the wait time.
+ if ( this.get('isSubscriber') || this.get('isModeratorOrHigher') || ! this.get('slowMode') )
+ this.updateWait(0, was_banned)
+ else if ( this.get('slowMode') )
+ this.updateWait(this.get('slow'));
+ }
+ }
+
// Message Filtering
var i = f._chat_filters.length;
@@ -1896,6 +2019,11 @@ FFZ.prototype._modify_room = function(room) {
if ( f._chat_filters[i](msg) === false )
return;
+
+ // Also update chatters.
+ if ( ! is_whisper && this.chatters && ! this.chatters[msg.from] && msg.from !== 'twitchnotify' && msg.from !== 'jtv' )
+ this.ffzUpdateChatters(msg.from);
+
// We're past the last return, so store the message
// now that we know we're keeping it.
if ( msg_id ) {
@@ -1913,8 +2041,15 @@ FFZ.prototype._modify_room = function(room) {
if ( window !== window.parent && parent.postMessage && msg.from && msg.from !== "jtv" && msg.from !== "twitchnotify" )
parent.postMessage({from_ffz: true, command: 'chat_message', data: {from: msg.from, room: msg.room}}, "*"); //location.protocol + "//www.twitch.tv/");
- // Add the message.
- return this._super(msg);
+ // Flagging for review.
+ if ( msg.tags && msg.tags.risk === "high" )
+ msg.flaggedForReview = true;
+
+ // Add the message. We don't do super anymore because it does stupid stuff.'
+ this.pushMessage(msg);
+ msg.from && msg.style !== "admin" && msg.style !== "notification" && msg.tags && this.addChatter(msg);
+ this.trackLatency(msg);
+ //return this._super(msg);
},
ffzChatFilters: function(msg) {
@@ -1934,7 +2069,7 @@ FFZ.prototype._modify_room = function(room) {
return this._super(e);
},
- send: function(text, ignore_history) {
+ send: function(text, ignore_history, used_aliases) {
try {
this.ffz_last_input = Date.now();
@@ -1958,15 +2093,41 @@ FFZ.prototype._modify_room = function(room) {
mru.unshift(text);
}
- var cmd = text.split(' ', 1)[0].toLowerCase();
- if ( cmd === "/ffz" ) {
- this.set("messageToSend", "");
- f.run_ffz_command(text.substr(5), this.get('id'));
- return;
+ if ( is_cmd ) {
+ var cmd = text.substr(1).split(' ', 1)[0].toLowerCase(),
+ was_handled = false;
- } else if ( cmd.charAt(0) === "/" && f.run_command(text, this.get('id')) ) {
- this.set("messageToSend", "");
- return;
+ if ( cmd === "ffz" ) {
+ f.run_ffz_command(text.substr(5), this.get('id'));
+ was_handled = true;
+
+ } else if ( f._command_aliases[cmd] ) {
+ used_aliases = used_aliases || [];
+ if ( used_aliases.indexOf(cmd) !== -1 ) {
+ f.room_message(f.rooms[this.get('id')], "Error: Your command aliases are recursing. [Path: " + used_aliases.join(", ") + "]");
+ was_handled = true;
+
+ } else {
+ var alias = f._command_aliases[cmd],
+ args = text.substr(1 + cmd.length).trimLeft().split(/\s+/g),
+ output = utils.replace_cmd_variables(alias, null, this, null, args);
+
+ used_aliases.push(cmd);
+ this.set("messageToSend", "");
+ var lines = output.split(/\s*\s*/g);
+ for(var i=0; i < lines.length; i++)
+ this.send(lines[i], true, used_aliases);
+
+ return;
+ }
+
+ } else if ( f.run_command(text, this.get('id')) )
+ was_handled = true;
+
+ if ( was_handled ) {
+ this.set("messageToSend", "");
+ return;
+ }
}
} catch(err) {
@@ -2130,10 +2291,6 @@ FFZ.prototype._modify_room = function(room) {
// Room State Stuff
- slowMode: function() {
- return this.get('slow') > 0;
- }.property('slow'),
-
onSlowOff: function() {
if ( ! this.get('slowMode') )
this.updateWait(0);
diff --git a/src/ember/vod-chat.js b/src/ember/vod-chat.js
index 8f23a566..2a5ce037 100644
--- a/src/ember/vod-chat.js
+++ b/src/ember/vod-chat.js
@@ -45,12 +45,12 @@ FFZ.prototype.setup_vod_chat = function() {
f.error("Unable to locate VOD Chat Service.");
this.update_views('component:vod-right-column', this.modify_vod_right_column);
- this.update_views('view:vod', this.modify_vod_view);
+ //this.update_views('view:vod', this.modify_vod_view);
this.update_views('component:vod-chat-display', this.modify_vod_chat_display);
}
-FFZ.prototype.modify_vod_view = function(view) {
+/*FFZ.prototype.modify_vod_view = function(view) {
var f = this;
utils.ember_reopen_view(view, {
ffz_init: function() {
@@ -90,7 +90,7 @@ FFZ.prototype.modify_vod_view = function(view) {
document.body.classList.toggle('ffz-small-player', f.settings.small_player && top >= height);
}
});
-}
+}*/
FFZ.prototype.modify_vod_right_column = function(component) {
diff --git a/src/ext/api.js b/src/ext/api.js
index 9e6b4770..c086b972 100644
--- a/src/ext/api.js
+++ b/src/ext/api.js
@@ -92,7 +92,7 @@ FFZ.prototype.api = function(name, icon, version) {
try {
this._known_apis = JSON.parse(localStorage.ffz_known_apis);
} catch(err) {
- this.log("Error loading Known APIs: " + err);
+ this.error("Error loading Known APIs", err);
}
}
@@ -105,6 +105,11 @@ API.prototype.log = function(msg, data, to_json, log_json) {
}
+API.prototype.error = function(msg, error, to_json, log_json) {
+ this.ffz.error('Ext "' + this.name + '": ' + msg, data, to_json, log_json);
+}
+
+
// ---------------------
// Set Loading
// ---------------------
@@ -115,8 +120,8 @@ API.prototype._load_set = function(real_id, set_id, data) {
// Check for an existing set to copy the users.
var users = [];
- if ( this.emote_sets[real_id] && this.emote_sets[real_id].users )
- users = this.emote_sets[real_id].users;
+ if ( this.emote_sets[set_id] && this.emote_sets[set_id].users )
+ users = this.emote_sets[set_id].users;
var emote_set = {
source: this.name,
@@ -127,14 +132,16 @@ API.prototype._load_set = function(real_id, set_id, data) {
emoticons: {},
_type: data._type || 0,
css: data.css || null,
+ hidden: data.hidden || false,
description: data.description || null,
icon: data.icon || this.icon || null,
id: real_id,
title: data.title || "Global Emoticons",
};
- this.emote_sets[real_id] = emote_set;
+ this.emote_sets[set_id] = emote_set;
+ // Use the real ID for FFZ's own tracking.
if ( this.ffz.emote_sets )
this.ffz.emote_sets[real_id] = emote_set;
@@ -202,6 +209,7 @@ API.prototype._load_set = function(real_id, set_id, data) {
emoticons[id] = new_emote;
}
+ // Use the real ID for building CSS.
utils.update_css(this.ffz._emote_style, real_id, output_css + (emote_set.css || ""));
if ( this.ffz._cindex )
@@ -231,21 +239,21 @@ API.prototype.load_set = function(id, emote_set) {
}
-API.prototype.unload_set = function(id) {
- var exact_id = this.id + '-' + id,
- emote_set = this.emote_sets[exact_id];
+API.prototype.unload_set = function(set_id) {
+ var exact_id = this.id + '-' + set_id,
+ emote_set = this.emote_sets[set_id];
if ( ! emote_set )
return;
// First, let's unregister it as a global.
- this.unregister_global_set(id);
+ this.unregister_global_set(set_id);
// Now, remove the set data.
utils.update_css(this.ffz._emote_style, exact_id, null);
- this.emote_sets[exact_id] = undefined;
+ this.emote_sets[set_id] = undefined;
if ( this.ffz.emote_sets )
this.ffz.emote_sets[exact_id] = undefined;
@@ -272,9 +280,8 @@ API.prototype.unload_set = function(id) {
}
-API.prototype.get_set = function(id) {
- var exact_id = this.id + '-' + id;
- return this.emote_sets[exact_id];
+API.prototype.get_set = function(set_id) {
+ return this.emote_sets[set_id];
}
@@ -282,14 +289,14 @@ API.prototype.get_set = function(id) {
// Global Emote Sets
// ---------------------
-API.prototype.register_global_set = function(id, emote_set) {
- var exact_id = this.id + '-' + id;
+API.prototype.register_global_set = function(set_id, emote_set) {
+ var exact_id = this.id + '-' + set_id;
if ( emote_set ) {
// If a set was provided, load it.
- emote_set = this.load_set(id, emote_set);
+ emote_set = this.load_set(set_id, emote_set);
} else
- emote_set = this.emote_sets[exact_id];
+ emote_set = this.emote_sets[set_id];
if ( ! emote_set )
throw new Error("Invalid set ID");
@@ -301,11 +308,11 @@ API.prototype.register_global_set = function(id, emote_set) {
// It's a valid set if we get here, so make it global.
- if ( this.global_sets.indexOf(exact_id) === -1 )
- this.global_sets.push(exact_id);
+ if ( this.global_sets.indexOf(set_id) === -1 )
+ this.global_sets.push(set_id);
- if ( this.default_sets.indexOf(exact_id) === -1 )
- this.default_sets.push(exact_id);
+ if ( this.default_sets.indexOf(set_id) === -1 )
+ this.default_sets.push(set_id);
if ( this.ffz.global_sets && this.ffz.global_sets.indexOf(exact_id) === -1 )
this.ffz.global_sets.push(exact_id);
@@ -319,19 +326,19 @@ API.prototype.register_global_set = function(id, emote_set) {
};
-API.prototype.unregister_global_set = function(id) {
- var exact_id = this.id + '-' + id,
- emote_set = this.emote_sets[exact_id];
+API.prototype.unregister_global_set = function(set_id) {
+ var exact_id = this.id + '-' + set_id,
+ emote_set = this.emote_sets[set_id];
if ( ! emote_set )
return;
// Remove the set from global sets.
- var ind = this.global_sets.indexOf(exact_id);
+ var ind = this.global_sets.indexOf(set_id);
if ( ind !== -1 )
this.global_sets.splice(ind,1);
- ind = this.default_sets.indexOf(exact_id);
+ ind = this.default_sets.indexOf(set_id);
if ( ind !== -1 )
this.default_sets.splice(ind,1);
@@ -353,8 +360,8 @@ API.prototype.unregister_global_set = function(id) {
// Per-Channel Emote Sets
// -----------------------
-API.prototype.register_room_set = function(room_id, id, emote_set) {
- var exact_id = this.id + '-' + id,
+API.prototype.register_room_set = function(room_id, set_id, emote_set) {
+ var exact_id = this.id + '-' + set_id,
room = this.ffz.rooms && this.ffz.rooms[room_id];
if ( ! room )
@@ -365,9 +372,9 @@ API.prototype.register_room_set = function(room_id, id, emote_set) {
emote_set.title = emote_set.title || "Channel: " + (room.display_name || room_id);
emote_set._type = emote_set._type || 1;
- emote_set = this.load_set(id, emote_set);
+ emote_set = this.load_set(set_id, emote_set);
} else
- emote_set = this.emote_sets[exact_id];
+ emote_set = this.emote_sets[set_id];
if ( ! emote_set )
throw new Error("Invalid set ID");
@@ -386,9 +393,9 @@ API.prototype.register_room_set = function(room_id, id, emote_set) {
}
-API.prototype.unregister_room_set = function(room_id, id) {
- var exact_id = this.id + '-' + id,
- emote_set = this.emote_sets[exact_id],
+API.prototype.unregister_room_set = function(room_id, set_id) {
+ var exact_id = this.id + '-' + set_id,
+ emote_set = this.emote_sets[set_id],
room = this.ffz.rooms && this.ffz.rooms[room_id];
if ( ! emote_set || ! room )
@@ -483,8 +490,8 @@ API.prototype.user_add_set = function(username, set_id) {
exact_id = this.id + '-' + set_id;
- if ( emote_sets.indexOf(exact_id) === -1 )
- emote_sets.push(exact_id);
+ if ( emote_sets.indexOf(set_id) === -1 )
+ emote_sets.push(set_id);
if ( ffz_sets.indexOf(exact_id) === -1 )
ffz_sets.push(exact_id);
@@ -505,7 +512,7 @@ API.prototype.user_remove_set = function(username, set_id) {
exact_id = this.id + '-' + set_id;
- var ind = emote_sets ? emote_sets.indexOf(exact_id) : -1;
+ var ind = emote_sets ? emote_sets.indexOf(set_id) : -1;
if ( ind !== -1 )
emote_sets.splice(ind, 1);
@@ -550,7 +557,7 @@ API.prototype._room_callbacks = function(room_id, room, specific_func) {
try {
specific_func(room_id, callback);
} catch(err) {
- this.log("Error in On-Room Callback: " + err);
+ this.error("Error in On-Room Callback", err);
}
} else {
@@ -559,7 +566,7 @@ API.prototype._room_callbacks = function(room_id, room, specific_func) {
try {
cb(room_id, callback);
} catch(err) {
- this.log("Error in On-Room Callback: " + err);
+ this.error("Error in On-Room Callback", err);
}
}
}
@@ -568,11 +575,16 @@ API.prototype._room_callbacks = function(room_id, room, specific_func) {
API.prototype.register_on_room_callback = function(callback, dont_iterate) {
this.on_room_callbacks.push(callback);
+ var register_callback = this.register_room_set.bind(this, room_id);
// Call this for all current rooms.
if ( ! dont_iterate && this.ffz.rooms ) {
for(var room_id in this.ffz.rooms)
- this._room_callbacks(room_id, this.ffz.rooms[room_id], callback);
+ try {
+ callback(room_id, register_callback);
+ } catch(err) {
+ this.error("Error in On-Room Callback", err);
+ }
}
}
diff --git a/src/ext/betterttv.js b/src/ext/betterttv.js
index a5c899f4..0706bf07 100644
--- a/src/ext/betterttv.js
+++ b/src/ext/betterttv.js
@@ -109,6 +109,9 @@ FFZ.prototype.setup_bttv = function(delay) {
cl.remove("ffz-portrait");
cl.remove("ffz-minimal-channel-title");
cl.remove("ffz-flip-dashboard");
+ cl.remove('ffz-minimal-channel-bar');
+ cl.remove('ffz-channel-bar-bottom');
+ cl.remove('ffz-channel-title-top');
// Remove Following Count
if ( this.settings.following_count ) {
@@ -270,8 +273,6 @@ FFZ.prototype.setup_bttv = function(delay) {
}
}
- f.log("BTTV Emotes", output);
-
return output;
}
diff --git a/src/main.js b/src/main.js
index 01cb9f1b..42d2c9a8 100644
--- a/src/main.js
+++ b/src/main.js
@@ -34,7 +34,7 @@ FFZ.msg_commands = {};
// Version
var VER = FFZ.version_info = {
- major: 3, minor: 5, revision: 283,
+ major: 3, minor: 5, revision: 302,
toString: function() {
return [VER.major, VER.minor, VER.revision].join(".") + (VER.extra || "");
}
@@ -249,7 +249,7 @@ FFZ.prototype.initialize = function(increment, delay) {
return this.init_clips(delay);
// Check for special non-ember pages.
- if ( /^\/(?:$|search$|team\/|user\/|p\/|settings|m\/|messages?\/)/.test(location.pathname) )
+ if ( /^\/(?:team\/|user\/|p\/|settings|m\/|messages?\/)/.test(location.pathname) )
return this.init_normal(delay);
// Check for the dashboard.
@@ -410,6 +410,7 @@ FFZ.prototype.init_dashboard = function(delay) {
// Set up the FFZ message passer.
this.setup_message_event();
+ this.cache_command_aliases();
this.fix_tooltips();
this.find_bttv(10);
@@ -433,12 +434,6 @@ FFZ.prototype.init_ember = function(delay) {
} catch(err) { this.embed_in_dash = false; }
- // Make an alias so they STOP RENAMING THIS ON ME
- var Settings = FFZ.utils.ember_lookup('controller:settings');
- if ( Settings && Settings.get('settings') === undefined )
- Settings.reopen({settings: Ember.computed.alias('model')});
-
-
// Settings are important.
this.load_settings();
@@ -448,10 +443,18 @@ FFZ.prototype.init_ember = function(delay) {
var f = this;
if ( Ember.RSVP && Ember.RSVP.on )
Ember.RSVP.on('error', function(error) {
+ // We want to ignore errors that are just 4xx HTTP responses.
+ if ( error && error.responseJSON && typeof error.responseJSON.status === "number" && error.responseJSON.status >= 400 )
+ return;
+
f.error("There was an error within an Ember RSVP.", error);
});
Ember.onerror = function(error) {
+ // We want to ignore errors that are just 4xx HTTP responses.
+ if ( error && error.responseJSON && typeof error.responseJSON.status === "number" && error.responseJSON.status >= 400 )
+ return;
+
f.error("There was an unknown error within Ember.", error);
}
}
@@ -505,6 +508,7 @@ FFZ.prototype.init_ember = function(delay) {
// Do all Ember modification before this point.
this.finalize_ember_wrapper();
+ this.cache_command_aliases();
this.fix_tooltips();
this.connect_extra_chat();
diff --git a/src/settings.js b/src/settings.js
index adc2fa53..0c174d40 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -815,8 +815,8 @@ FFZ.prototype._setting_get = function(key) {
}
FFZ.prototype._setting_get_twitch = function(key) {
- var Settings = utils.ember_lookup('controller:settings');
- return Settings && Settings.get('settings.' + key);
+ var Settings = utils.ember_settings();
+ return Settings && Settings.get(key);
}
diff --git a/src/socket.js b/src/socket.js
index c7fbaa47..900e1d76 100644
--- a/src/socket.js
+++ b/src/socket.js
@@ -192,18 +192,18 @@ FFZ.prototype.ws_create = function() {
if ( room.important ) {
f.ws_sub("room." + room_id);
- if ( room.needs_history ) {
+ /*if ( room.needs_history ) {
room.needs_history = false;
if ( ! f.has_bttv && f.settings.chat_history )
f.ws_send("chat_history", [room_id,25], f._load_history.bind(f, room_id));
- }
+ }*/
}
}
// Send the channel(s).
if ( f._cindex ) {
- var channel_id = f._cindex.get('controller.model.id'),
- hosted_id = f._cindex.get('controller.hostModeTarget.id');
+ var channel_id = f._cindex.get('channel.id'),
+ hosted_id = f._cindex.get('channel.hostModeTarget.id');
if ( channel_id )
f.ws_sub("channel." + channel_id);
@@ -463,14 +463,12 @@ FFZ.ws_commands.do_authorize = function(data) {
if ( ! this.rooms.hasOwnProperty(room_id) )
continue;
- var r = this.rooms[room_id];
+ var r = this.rooms[room_id],
+ c = r && r.room && r.room.tmiRoom && r.room.tmiRoom._getConnection();
- if ( r && r.room && !r.room.get('roomProperties.eventchat') && !r.room.get('isGroupRoom') && r.room.tmiRoom ) {
- var c = r.room.tmiRoom._getConnection();
- if ( c.isConnected ) {
- conn = c;
- break;
- }
+ if ( c.isConnected ) {
+ conn = c;
+ break;
}
}
@@ -478,5 +476,5 @@ FFZ.ws_commands.do_authorize = function(data) {
conn._send("PRIVMSG #frankerfacezauthorizer :AUTH " + data);
else
// Try again shortly.
- setTimeout(FFZ.ws_commands.do_authorize.bind(this, data), 5000);
+ setTimeout(FFZ.ws_commands.do_authorize.bind(this, data), 1000);
}
\ No newline at end of file
diff --git a/src/tokenize.js b/src/tokenize.js
index e5e93170..ce06e011 100644
--- a/src/tokenize.js
+++ b/src/tokenize.js
@@ -728,6 +728,10 @@ FFZ.prototype.tokenize_chat_line = function(msgObject, prevent_notification, del
// We have a mention!
msgObject.ffz_has_mention = true;
+ // If it's a historical message we don't want to update any other UI.
+ if ( msg.tags && msg.tags.historical )
+ continue;
+
// If we have chat tabs/rows, update the status.
if ( room_id && ! this.has_bttv && this._chatv ) {
var room = this.rooms[room_id] && this.rooms[room_id].room;
@@ -1227,7 +1231,7 @@ FFZ._words_to_regex = function(list) {
if ( ! list[i] )
continue;
- reg += (reg ? "|" : "") + reg_escape(list[i]);
+ reg += (reg ? "|" : "") + (list[i].substr(0,6) === "regex:" ? list[i].substr(6) : reg_escape(list[i]));
}
regex = FFZ._regex_cache[list] = new RegExp("(^|.*?" + constants.SEPARATORS + ")(" + reg + ")(?=$|" + constants.SEPARATORS + ")", "ig");
diff --git a/src/ui/dark.js b/src/ui/dark.js
index 2bab5d71..62c77c20 100644
--- a/src/ui/dark.js
+++ b/src/ui/dark.js
@@ -89,7 +89,7 @@ FFZ.basic_settings.keywords = {
help: "Set additional keywords that will be highlighted in chat.",
method: function() {
- FFZ.settings_info.keywords.method.call(this);
+ FFZ.settings_info.keywords.method.call(this, null, true);
}
};
@@ -103,7 +103,7 @@ FFZ.basic_settings.banned_words = {
help: "Set a list of words that will be removed from chat messages, locally.",
method: function() {
- FFZ.settings_info.banned_words.method.call(this);
+ FFZ.settings_info.banned_words.method.call(this, null, true);
}
};
@@ -137,15 +137,14 @@ FFZ.settings_info.dark_twitch = {
(this.is_clips ? document.querySelector('html') : document.body).classList.toggle("ffz-dark", val);
- var Settings = utils.ember_lookup('controller:settings'),
- settings = Settings && Settings.get('settings');
+ var Settings = utils.ember_settings();
if ( val ) {
this._load_dark_css();
- settings && this.settings.set('twitch_chat_dark', settings.get('darkMode'));
- settings && settings.set('darkMode', true);
+ Settings && this.settings.set('twitch_chat_dark', Settings.get('darkMode'));
+ Settings && Settings.set('darkMode', true);
} else
- settings && settings.set('darkMode', this.settings.twitch_chat_dark);
+ Settings && Settings.set('darkMode', this.settings.twitch_chat_dark);
// Try coloring chat replay
window.jQuery && jQuery('.chatReplay').toggleClass('dark', val || false);
@@ -203,10 +202,10 @@ FFZ.prototype.setup_dark = function() {
if ( ! this.settings.dark_twitch )
return;
- var Settings = utils.ember_lookup('controller:settings');
+ var Settings = utils.ember_settings();
if ( Settings ) {
try {
- Settings.set('settings.darkMode', true);
+ Settings.set('darkMode', true);
} catch(err) {
this.error("Unable to set the darkMode setting because it isn't named what we expect. WTF?");
}
diff --git a/src/ui/following.js b/src/ui/following.js
index b72145d0..1ac9466c 100644
--- a/src/ui/following.js
+++ b/src/ui/following.js
@@ -76,8 +76,8 @@ FFZ.ffz_commands.following = function(room, args) {
FFZ.ws_on_close.push(function() {
var controller = utils.ember_lookup('controller:channel'),
- current_id = controller && controller.get('content.id'),
- current_host = controller && controller.get('hostModeTarget.id'),
+ current_id = controller && controller.get('channelModel.id'),
+ current_host = controller && controller.get('channelModel.hostModeTarget.id'),
need_update = false;
this.follow_sets = {};
@@ -112,8 +112,8 @@ FFZ.ws_on_close.push(function() {
FFZ.ws_commands.follow_buttons = function(data) {
var controller = utils.ember_lookup('controller:channel'),
- current_id = controller && controller.get('content.id'),
- current_host = controller && controller.get('hostModeTarget.id'),
+ current_id = controller && controller.get('channelModel.id'),
+ current_host = controller && controller.get('channelModel.hostModeTarget.id'),
need_update = false;
this.follow_data = this.follow_data || {};
@@ -131,8 +131,8 @@ FFZ.ws_commands.follow_buttons = function(data) {
FFZ.ws_commands.follow_sets = function(data) {
var controller = utils.ember_lookup('controller:channel'),
- current_id = controller && controller.get('content.id'),
- current_host = controller && controller.get('hostModeTarget.id'),
+ current_id = controller && controller.get('channelModel.id'),
+ current_host = controller && controller.get('channelModel.hostModeTarget.id'),
need_update = false,
f = this;
@@ -187,13 +187,12 @@ FFZ.ws_commands.follow_sets = function(data) {
// ---------------
FFZ.prototype.rebuild_following_ui = function() {
- var controller = utils.ember_lookup('controller:channel'),
- channel_id = controller && controller.get('content.id'),
- hosted_id = controller && controller.get('hostModeTarget.id');
-
if ( ! this._cindex )
return;
+ var channel_id = this._cindex.get('channel.id'),
+ hosted_id = this._cindex.get('channel.hostModeTarget.id');
+
if ( channel_id ) {
var data = this.follow_data && this.follow_data[channel_id],
diff --git a/src/ui/menu.js b/src/ui/menu.js
index b12ce9bc..06f652be 100644
--- a/src/ui/menu.js
+++ b/src/ui/menu.js
@@ -45,42 +45,20 @@ FFZ.prototype.setup_menu = function() {
this.log("Hooking the Ember Chat Settings view.");
- var Settings = utils.ember_resolve('view:settings'),
+ var Settings = utils.ember_resolve('component:chat/chat-settings-menu'),
Layout = utils.ember_lookup('service:layout'),
f = this;
if ( ! Settings )
return;
- Settings.reopen({
- didInsertElement: function() {
- this._super();
-
- try {
- this.ffzInit();
- } catch(err) {
- f.error("ChatSettings didInsertElement: " + err);
- }
- },
-
- willClearRender: function() {
- try {
- this.ffzTeardown();
- } catch(err) {
- f.error("ChatSettings willClearRender: " + err);
- }
- this._super();
- },
-
- ffzInit: function() {
+ utils.ember_reopen_view(Settings, {
+ ffz_init: function() {
var view = this,
- el = this.get('element'),
- menu = el && el.querySelector('.dropmenu');
+ el = this.get('element');
- if ( ! menu )
- return;
-
- var header = utils.createElement('div', 'list-header', 'FrankerFaceZ'),
+ var container = utils.createElement('div', ''),
+ header = utils.createElement('div', 'list-header', 'FrankerFaceZ'),
content = utils.createElement('div', 'chat-menu-content'),
p, cb, a;
@@ -125,25 +103,35 @@ FFZ.prototype.setup_menu = function() {
content.appendChild(p);
a.addEventListener('click', function(e) {
- view.set('controller.settings.hidden', true);
+ view.set('isHidden', true);
f._last_page = 'settings';
f.build_ui_popup(f._chatv);
e.stopPropagation();
return false;
});
- menu.appendChild(header);
- menu.appendChild(content);
+ container.appendChild(header);
+ container.appendChild(content);
+
+ container.classList.toggle('hidden', this.get('showDisplaySettings'));
+ el.appendChild(container);
+ this.ffz_menu = container;
// Maximum Height
- var e = el.querySelector('.chat-settings');
- if ( Layout && e )
- e.style.maxHeight = (Layout.get('windowHeight') - 90) + 'px';
-
+ if ( Layout && el )
+ el.style.maxHeight = (Layout.get('windowHeight') - 90) + 'px';
},
- ffzTeardown: function() {
- // Nothing~!
+ ffz_update_visibility: function() {
+ if ( this.ffz_menu )
+ this.ffz_menu.classList.toggle('hidden', this.get('showDisplaySettings'));
+ }.observes('showDisplaySettings'),
+
+ ffz_destroy: function() {
+ if ( this.ffz_menu ) {
+ this.ffz_menu.parentElement.removeChild(this.ffz_menu);
+ this.ffz_menu = null;
+ }
}
});
@@ -648,6 +636,9 @@ FFZ.menu_pages.channel = {
var set = this.emote_sets[extra_sets[i]],
name = set ? (set.hasOwnProperty('source_ext') ? "" : "Featured ") + set.title : "Featured Channel";
+ if ( ! set || ! set.count || set.hidden )
+ continue;
+
this._emotes_for_sets(inner, view, [extra_sets[i]], name, set.icon || "//cdn.frankerfacez.com/script/devicon.png", set.source || "FrankerFaceZ");
}
diff --git a/src/ui/my_emotes.js b/src/ui/my_emotes.js
index 724ec59f..768dfb0b 100644
--- a/src/ui/my_emotes.js
+++ b/src/ui/my_emotes.js
@@ -224,7 +224,7 @@ FFZ.menu_pages.myemotes = {
if ( favorites_only && (! favorites_list || ! favorites_list.length) )
continue;
- if ( ! set || ! set.count || ( ! this.settings.global_emotes_in_menu && this.default_sets.indexOf(set_id) !== -1 ) )
+ if ( ! set || ! set.count || set.hidden || ( ! this.settings.global_emotes_in_menu && this.default_sets.indexOf(set_id) !== -1 ) )
continue;
var menu = FFZ.menu_pages.myemotes.draw_ffz_set.call(this, view, set, favorites_only);
diff --git a/src/ui/races.js b/src/ui/races.js
index bcd1ad8b..9032f5d8 100644
--- a/src/ui/races.js
+++ b/src/ui/races.js
@@ -36,8 +36,8 @@ FFZ.settings_info.srl_races = {
FFZ.ws_on_close.push(function() {
var controller = utils.ember_lookup('controller:channel'),
- current_id = controller && controller.get('content.id'),
- current_host = controller && controller.get('hostModeTarget.id'),
+ current_id = controller && controller.get('channelModel.id'),
+ current_host = controller && controller.get('channelModel.hostModeTarget.id'),
need_update = false;
if ( ! controller )
@@ -56,8 +56,8 @@ FFZ.ws_on_close.push(function() {
FFZ.ws_commands.srl_race = function(data) {
var controller = utils.ember_lookup('controller:channel'),
- current_id = controller && controller.get('content.id'),
- current_host = controller && controller.get('hostModeTarget.id'),
+ current_id = controller && controller.get('channelModel.id'),
+ current_host = controller && controller.get('channelModel.hostModeTarget.id'),
need_update = false;
this.srl_races = this.srl_races || {};
@@ -91,13 +91,12 @@ FFZ.ws_commands.srl_race = function(data) {
// ---------------
FFZ.prototype.rebuild_race_ui = function() {
- var controller = utils.ember_lookup('controller:channel'),
- channel_id = controller && controller.get('content.id'),
- hosted_id = controller && controller.get('hostModeTarget.id');
-
if ( ! this._cindex )
return;
+ var channel_id = this._cindex.get('channel.id'),
+ hosted_id = this._cindex.get('channel.hostModeTarget.id');
+
if ( channel_id ) {
var race = this.srl_races && this.srl_races[channel_id],
diff --git a/src/utils.js b/src/utils.js
index e9b71d6a..edbf3fc9 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -267,7 +267,20 @@ var createElement = function(tag, className, content) {
FrankerFaceZ.get().error("There was an error looking up an Ember instance: " + thing, err);
return null;
}
- };
+ },
+
+ ember_resolve = function(thing) {
+ if ( ! window.App )
+ return;
+
+ if ( App.__deprecatedInstance__ && App.__deprecatedInstance__.registry && App.__deprecatedInstance__.registry.resolve )
+ return App.__deprecatedInstance__.registry.resolve(thing);
+ if ( App.__container__ && App.__container__.resolve )
+ return App.__container__.resolve(thing);
+ },
+
+
+ CMD_VAR_REGEX = /{(\d+(?:\$(?:\d+)?)?|id|msg_id|message_id|(?:user|room)(?:_id|_name|_display_name)?)}/g;
module.exports = FFZ.utils = {
@@ -277,15 +290,10 @@ module.exports = FFZ.utils = {
},
ember_lookup: ember_lookup,
-
- ember_resolve: function(thing) {
- if ( ! window.App )
- return;
-
- if ( App.__deprecatedInstance__ && App.__deprecatedInstance__.registry && App.__deprecatedInstance__.registry.resolve )
- return App.__deprecatedInstance__.registry.resolve(thing);
- if ( App.__container__ && App.__container__.resolve )
- return App.__container__.resolve(thing);
+ ember_resolve: ember_resolve,
+ ember_settings: function() {
+ var settings = ember_resolve('model:settings');
+ return settings && settings.findOne();
},
ember_reopen_view: function(component, data) {
@@ -332,6 +340,62 @@ module.exports = FFZ.utils = {
},
+ find_parent: function(el, klass) {
+ while (el && el.parentNode) {
+ el = el.parentNode;
+ if ( el.classList.contains(klass) )
+ return el;
+ }
+
+ return null;
+ },
+
+
+ CMD_VAR_REGEX: CMD_VAR_REGEX,
+
+ replace_cmd_variables: function(command, user, room, message, args) {
+ user = user || {};
+ room = room || {};
+ message = message || {};
+ message.tags = message.tags || {};
+
+ var msg_id = message.tags.id,
+ replacements = {
+ user: user.name,
+ user_name: user.name,
+ user_display_name: user.display_name || message.tags['display-name'],
+ user_id: user._id || message.tags['user-id'],
+
+ room: room.id,
+ room_name: room.id,
+ room_display_name: room.get && (room.get('tmiRoom.displayName') || room.get('channel.displayName')),
+ room_id: room.get && room.get('roomProperties._id') || message.tags['room-id'],
+
+ id: msg_id,
+ message_id: msg_id,
+ msg_id: msg_id
+ };
+
+ CMD_VAR_REGEX.lastIndex = 0;
+ return command.replace(CMD_VAR_REGEX, function(match, variable) {
+ if ( replacements[variable] )
+ return replacements[variable];
+
+ if ( args ) {
+ var match = /(\d+)(?:(\$)(\d+)?)?/.exec(variable);
+ if ( match ) {
+ var num = parseInt(match[1]),
+ second_num = match[3] ? parseInt(match[3]) : undefined;
+
+ return match[2] === '$' ? args.slice(num, second_num).join(" ") : args[num];
+ }
+ }
+
+ return '{' + variable + '}';
+ });
+ },
+
+
show_modal: show_modal,
confirm: function(title, description, callback) {
var contents = createElement('div', 'text-content'),
diff --git a/style.css b/style.css
index 1777605c..51c15b40 100644
--- a/style.css
+++ b/style.css
@@ -18,6 +18,10 @@ body > div.tipsy .tipsy-arrow { opacity: 0.8; }
cursor: pointer;
}
+.ffz-minimal-channel-title .cn-metabar__title:not(:hover) .card__info,
+.ffz-minimal-channel-bar .cn-bar-spacer,
+.ffz-channel-bar-bottom .cn-bar-spacer,
+.ffz-channel-bar-bottom .cn-cover-wrap,
.app-main .ad_leader:empty,
body:not(.ffz-show-bits-tags) .bits-tag--container,
.ffz-hide-friends nav .friend-list,
@@ -28,12 +32,37 @@ body:not(.ffz-show-bits-tags) .bits-tag--container,
.ffz-hide-promoted-games .promotedGames,
.ffz-hide-recent-past-broadcast .recent-past-broadcast,
.ffz-hide-view-count .stat.twitch-channel-views,
+.ffz-hide-view-count .cn-metabar__viewcount,
.ffz-minimal-chat-input .chat-interface .emoticon-selector-toggle,
.ffz-menu-replace .chat-interface .ember-emoticon-selector,
.ffz-menu-replace .chat-interface .emoticon-selector-toggle {
display: none !important;
}
+.ffz-channel-bar-bottom #channel {
+ margin-bottom: 60px;
+}
+
+.ffz-channel-bar-bottom .notification-controls .balloon {
+ top: auto;
+ bottom: 50px;
+}
+
+.ffz-channel-bar-bottom .js-username-hover-tip .balloon {
+ top: auto;
+ bottom: 100px;
+}
+
+.ffz-channel-bar-bottom .cn-bar {
+ top: auto;
+ bottom: 0;
+ box-shadow: inset 0 1px 0 #e5e3e8,0 -1px -1px rgba(0,0,0,.065);
+}
+
+.ffz-dark.ffz-channel-bar-bottom .cn-bar {
+ box-shadow: inset 0 1px 0 #161616, -1px -1px rgba(255,255,255,0.065);
+}
+
body:not(.ffz-show-bits-tags) .ember-chat .chat-messages.bits-tags__offset {
top: 0;
}
@@ -223,31 +252,18 @@ body.ffz-bttv-dark .ffz-ui-toggle.blue.live:hover svg.svg-emoticons path { fill:
/* Minimal Channel Title */
-.ffz-minimal-channel-title #broadcast-meta {
- height: 25px;
- margin: 20px 0;
+.ffz-minimal-channel-title .cn-metabar__title:hover {
+ background-color: #fff;
+ padding: 1rem;
+ margin-left: -1rem;
}
+.ffz-dark.ffz-minimal-channel-title .cn-metabar__title:hover { background-color: #101010 }
-.ffz-minimal-channel-title #channel #broadcast-meta .profile-link .profile-photo img {
- width: 25px; height: 25px;
+.ffz-minimal-channel-title .cn-metabar__title:not(:hover) .card__img--boxart {
+ height: 20px !important;
+ width: 14px !important;
}
-.ffz-minimal-channel-title #channel #broadcast-meta .info {
- padding-top: 25px;
- padding-left: 50px;
-}
-
-.ffz-minimal-channel-title #channel #broadcast-meta .info .title {
- padding-top: 0;
- margin: 0;
- left: calc(25px + 1.5rem);
- height: 25px;
- min-height: 25px;
-}
-
-.ffz-minimal-channel-title #broadcast-meta .profile-photo .goto,
-.ffz-minimal-channel-title #broadcast-meta .channel { display: none }
-
/* Theater Mode hover bar */
@@ -1404,6 +1420,7 @@ img.channel_background[src="null"] { display: none; }
background-color: #232329;
}
+.ffz-no-blue .cn-hosting,
.ffz-no-blue .dark .ember-chat .moderation-card .interface,
.ffz-no-blue .force-dark .ember-chat .moderation-card .interface,
.ffz-no-blue .theatre .ember-chat .moderation-card .interface {
@@ -1426,8 +1443,13 @@ img.channel_background[src="null"] { display: none; }
text-decoration: none;
font-size: 18px;
font-weight: bold;
- margin-top: -1px !important;
+ margin-top: -1px !important;
color: #888 !important;
+ background: none !important;
+}
+
+.mod-icons .mod-icon img {
+ pointer-events: none;
}
@@ -1550,6 +1572,8 @@ body:not(.ffz-bttv) .chat-container:not(.chatReplay) .more-messages-indicator {
/* Menu About Page */
+#ffz-chat-menu .center { text-align: center }
+
.ffz-about-table {
width: 100%;
}
@@ -2039,8 +2063,11 @@ body.ffz-minimal-chat-input .ember-chat .chat-interface .textarea-contain textar
line-height: 30px;
margin-left: -10px;
margin-right: 15px;
+ cursor: default;
}
+.ffz.room-state.faded { opacity: 0.5 }
+
.ffz.room-state.truncated span { font-size: 8px; }
.button.primary.ffz-waiting:not(:hover) {
@@ -2082,6 +2109,14 @@ body:not([data-current-path^="user."]) .ffz-sidebar-swap .ember-chat .chat-inter
left: 20px;
}
+.ffz-sidebar-swap #main_col .cn-bar-fixed {
+ right: 50px;
+}
+
+.ffz-sidebar-swap #left_col.open ~ #main_col .cn-bar-fixed {
+ right: 240px;
+}
+
.ffz-sidebar-swap #left_col {
left: auto;
right: 0;
@@ -3234,4 +3269,65 @@ body.ffz-bttv #ffz-feed-tabs .tabs { margin-bottom: 0 }
background: #101010;
color: #ccc;
border-color: rgba(255,255,255,0.2);
+}
+
+.ffz-mod-balloon {
+ display: block;
+ max-width: 200px;
+}
+
+.ffz-mod-balloon .ffz-title {
+ padding: 0 1rem;
+}
+
+
+.cn-metabar__ffz svg { fill: #898395; }
+.cn-metabar__ffz {
+ cursor: default;
+ color: #706a7c;
+ font-size: 1.4rem;
+}
+.cn-metabar__ffz figure {
+ margin-right: 5px;
+}
+
+.ffz-channel-title-top .cn-metabar__more { width: 100% }
+.ffz-channel-title-top #channel {
+ position: relative;
+ padding-top: 80px;
+}
+
+.ffz-minimal-channel-title.ffz-channel-title-top #channel {
+ padding-top: 50px;
+}
+
+.ffz-channel-title-top .cn-metabar__title {
+ position: absolute;
+ top: 5px;
+}
+
+.ffz-channel-title-top:not(.ffz-channel-bar-bottom) #channel.ffz-bar-fixed .cn-metabar__title {
+ margin-top: 50px;
+}
+
+.ffz-channel-title-top .cn-metabar__more > div { order: 999; }
+.ffz-channel-title-top .cn-metabar__more > span:last-of-type { flex-grow: 1 }
+.ffz-channel-title-top .cn-metabar__more > span:last-of-type > div { float: left }
+.ffz-channel-title-top .cn-metabar__more > div:last-of-type { margin-right: 0 !important }
+.ffz-channel-title-top .cn-metabar__more > div + button:first-of-type,
+.ffz-channel-title-top .cn-metabar__more > div + span:first-of-type button.mg-l-1 { margin-left: 0 !important }
+.ffz-channel-title-top .cn-metabar__viewcount { flex-grow: 0 !important; }
+
+.ffz-minimal-channel-bar .cn-bar-fixed {
+ top: -40px;
+ transition: top ease-in-out 75ms, bottom ease-in-out 75ms;
+}
+
+.ffz-minimal-channel-bar .cn-bar-fixed:hover { top: 0 }
+.ffz-minimal-channel-bar.ffz-channel-bar-bottom .cn-bar-fixed { top: auto; bottom: -40px }
+.ffz-minimal-channel-bar.ffz-channel-bar-bottom .cn-bar-fixed:hover { bottom: 0 }
+
+.ffz-minimal-channel-title .cn-metabar,
+.ffz-channel-title-top .cn-metabar {
+ min-height: 70px;
}
\ No newline at end of file