1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-06-29 07:45:33 +00:00

Chat input is in now, and a ton of other stuff. Why are my commits to trash?

This commit is contained in:
SirStendec 2015-07-06 00:09:21 -04:00
parent 6a62804ec1
commit a7e7f7498d
18 changed files with 1416 additions and 165 deletions

View file

@ -470,7 +470,8 @@
.ffz-dark .chat-menu.ffz-ui-popup .ffz-ui-menu-page .chat-menu-content .heading, .ffz-dark .chat-menu.ffz-ui-popup .ffz-ui-menu-page .chat-menu-content .heading,
.ffz-dark .chat-menu.ffz-ui-popup .ffz-ui-menu-page .emoticon-grid .heading, .ffz-dark .chat-menu.ffz-ui-popup .ffz-ui-menu-page .emoticon-grid .heading,
.ffz-dark .ffz-ui-popup ul.menu, .ffz-dark .ffz-ui-popup ul.menu,
.ffz-dark .ffz-ui-popup ul.menu a { .ffz-dark .ffz-ui-popup ul.menu a,
.ffz-sidebar-swap.ffz-dark .ffz-ui-popup ul.menu a {
border-color: #32323e; border-color: #32323e;
} }
@ -666,6 +667,10 @@
/* Dashboard */ /* Dashboard */
.ffz-dark .js-stream-key-button-container .button {
margin: 15px auto;
}
.ffz-dark .js-show-stream-key, .ffz-dark .js-show-stream-key,
.ffz-dark .js-reset-stream-key { .ffz-dark .js-reset-stream-key {
display: block; display: block;
@ -687,6 +692,11 @@
margin: 10px auto; margin: 10px auto;
} }
.ffz-dark .stream-key {
background: #000;
color: #f2f2f2;
}
.ffz-dark .js-show-stream-key:hover, .ffz-dark .js-show-stream-key:hover,
.ffz-dark .js-show-stream-key:focus { background-color: rgb(192,102,0); } .ffz-dark .js-show-stream-key:focus { background-color: rgb(192,102,0); }
@ -807,16 +817,4 @@
.ffz-dark .legal_page ol.legal li p { .ffz-dark .legal_page ol.legal li p {
color: #ccc; color: #ccc;
} }
/* TEST
.ffz-dark div#main_col .tse-scroll-content {
background-image:
linear-gradient(rgba(16,16,16,0.75), rgba(16,16,16,1)),
url("http://i.imgur.com/DzCLo3d.jpg");
background-attachment: fixed;
background-size: 100% auto;
background-repeat: no-repeat;
}*/

749
script.js

File diff suppressed because one or more lines are too long

12
script.min.js vendored

File diff suppressed because one or more lines are too long

View file

@ -17,11 +17,31 @@ FFZ.settings_info.show_badges = {
}; };
FFZ.settings_info.transparent_badges = {
type: "boolean",
value: false,
category: "Chat",
no_bttv: true,
name: "Transparent Badges",
help: "Make chat badges transparent for a nice, clean look. On light chat, non-subscriber badges are inverted to remain visible.",
on_update: function(val) {
if ( ! this.has_bttv )
document.body.classList.toggle("ffz-transparent-badges", val);
}
};
// -------------------- // --------------------
// Initialization // Initialization
// -------------------- // --------------------
FFZ.prototype.setup_badges = function() { FFZ.prototype.setup_badges = function() {
if ( ! this.has_bttv )
document.body.classList.toggle("ffz-transparent-badges", this.settings.transparent_badges);
this.log("Preparing badge system."); this.log("Preparing badge system.");
this.badges = {}; this.badges = {};
@ -65,7 +85,10 @@ FFZ.ws_commands.set_badge = function(data) {
// -------------------- // --------------------
var badge_css = function(badge) { var badge_css = function(badge) {
return ".badges .ffz-badge-" + badge.id + " { background-color: " + badge.color + '; background-image: url("' + badge.image + '"); ' + (badge.extra_css || "") + '}'; var out = ".badges .ffz-badge-" + badge.id + " { background-color: " + badge.color + '; background-image: url("' + badge.image + '"); ' + (badge.extra_css || "") + '}';
if ( badge.transparent_image )
out += ".ffz-transparent-badges .badges .ffz-badge-" + badge.id + ' { background-image: url("' + badge.transparent_image + '"); }';
return out;
} }
@ -256,11 +279,11 @@ FFZ.bttv_known_bots = ["nightbot","moobot","sourbot","xanbot","manabot","mtgbot"
FFZ.prototype._legacy_add_donors = function() { FFZ.prototype._legacy_add_donors = function() {
// Developer Badge // Developer Badge
this.badges[0] = {id: 0, title: "FFZ Developer", color: "#FAAF19", image: "//cdn.frankerfacez.com/script/devicon.png"}; this.badges[0] = {id: 0, title: "FFZ Developer", color: "#FAAF19", image: "//cdn.frankerfacez.com/script/devicon.png", transparent_image: "//cdn.frankerfacez.com/script/devtransicon.png"};
utils.update_css(this._badge_style, 0, badge_css(this.badges[0])); utils.update_css(this._badge_style, 0, badge_css(this.badges[0]));
// Donor Badge // Donor Badge
this.badges[1] = {id: 1, title: "FFZ Donor", color: "#755000", image: "//cdn.frankerfacez.com/script/donoricon.png"}; this.badges[1] = {id: 1, title: "FFZ Donor", color: "#755000", image: "//cdn.frankerfacez.com/script/devicon.png"};
utils.update_css(this._badge_style, 1, badge_css(this.badges[1])); utils.update_css(this._badge_style, 1, badge_css(this.badges[1]));
// Bot Badge // Bot Badge

File diff suppressed because one or more lines are too long

View file

@ -141,6 +141,9 @@ FFZ.prototype._modify_cindex = function(view) {
el.setAttribute('data-channel', id); el.setAttribute('data-channel', id);
el.classList.add('ffz-channel'); el.classList.add('ffz-channel');
// Try changing the theater mode tooltip.
this.$('.theatre-button a').attr('title', 'Theater Mode (Alt+T)');
this.ffzFixTitle(); this.ffzFixTitle();
this.ffzUpdateUptime(); this.ffzUpdateUptime();
this.ffzUpdateChatters(); this.ffzUpdateChatters();

333
src/ember/chat-input.js Normal file
View file

@ -0,0 +1,333 @@
var FFZ = window.FrankerFaceZ,
utils = require("../utils"),
constants = require("../constants"),
KEYCODES = {
BACKSPACE: 8,
TAB: 9,
ENTER: 13,
ESC: 27,
SPACE: 32,
LEFT: 37,
UP: 38,
RIGHT: 39,
DOWN: 40,
TWO: 50,
COLON: 186
},
selection_start = function(e) {
if ( typeof e.selectionStart === "number" )
return e.selectionStart;
if ( ! e.createTextRange )
return -1;
var n = document.selection.createRange(),
r = e.createTextRange();
r.moveToBookmark(n.getBookmark());
r.moveStart("character", -e.value.length);
return r.text.length;
},
move_selection = function(e, pos) {
if ( e.setSelectionRange )
e.setSelectionRange(pos, pos);
else if ( e.createTextRange ) {
var r = e.createTextRange();
r.move("character", -e.value.length);
r.move("character", pos);
r.select();
}
};
// ---------------------
// Settings
// ---------------------
FFZ.settings_info.input_quick_reply = {
type: "boolean",
value: true,
category: "Chat Input",
no_bttv: true,
name: "Reply to Whispers with /r",
help: "Automatically replace /r at the start of the line with the command to whisper to the person you've whispered with most recently."
};
FFZ.settings_info.input_mru = {
type: "boolean",
value: true,
category: "Chat Input",
no_bttv: true,
name: "Chat Input History",
help: "Use the Up and Down arrows in chat to select previously sent chat messages."
};
FFZ.settings_info.input_emoji = {
type: "boolean",
value: false,
category: "Chat Input",
visible: false,
no_bttv: true,
name: "Enter Emoji By Name",
help: "Replace emoji that you type by name with the character. :+1: becomes 👍."
};
// ---------------------
// Initialization
// ---------------------
FFZ.prototype.setup_chat_input = function() {
this.log("Hooking the Ember Chat Input controller.");
var Input = App.__container__.resolve('component:twitch-chat-input'),
f = this;
if ( ! Input )
return;
this._modify_chat_input(Input);
if ( this._roomv ) {
for(var i=0; i < this._roomv._childViews.length; i++) {
var v = this._roomv._childViews[i];
if ( v instanceof Input ) {
this._modify_chat_input(v);
v.ffzInit();
}
}
}
}
FFZ.prototype._modify_chat_input = function(component) {
var f = this;
component.reopen({
ffz_mru_index: -1,
didInsertElement: function() {
this._super();
try {
this.ffzInit();
} catch(err) { f.error("ChatInput didInsertElement: " + err); }
},
willClearRender: function() {
try {
this.ffzTeardown();
} catch(err) { f.error("ChatInput willClearRender: " + err); }
return this._super();
},
ffzInit: function() {
f._inputv = this;
// Redo our key bindings.
var t = this.$("textarea");
t.off("keydown");
//t.off("keyup");
t.on("keydown", this._ffzKeyDown.bind(this));
//t.on("keyup", this._ffzKeyUp.bind(this));
/*var suggestions = this._parentView.get('context.model.chatSuggestions');
this.set('ffz_chatters', suggestions);*/
},
ffzTeardown: function() {
if ( f._inputv === this )
f._inputv = undefined;
// Reset normal key bindings.
var t = this.$("textarea");
t.off("keydown");
t.on("keydown", this._onKeyDown.bind(this));
//t.on("keyup", this._onKeyUp.bind(this));
},
// Input Control
_ffzKeyDown: function(event) {
var e = event || window.event,
key = e.charCode || e.keyCode;
switch(key) {
case KEYCODES.UP:
case KEYCODES.DOWN:
if ( this.get("isShowingSuggestions") )
e.preventDefault();
else if ( f.settings.input_mru )
Ember.run.next(this.ffzCycleMRU.bind(this, key));
else
return this._onKeyDown(event);
break;
case KEYCODES.SPACE:
if ( f.settings.input_quick_reply && selection_start(this.get("chatTextArea")) === 2 && this.get("textareaValue").substring(0,2) === "/r" ) {
var t = this;
Ember.run.next(function() {
var wt = t.get("uniqueWhisperSuggestions.0");
if ( wt ) {
var text = "/w " + wt + t.get("textareaValue").substr(2);
t.set("_currentWhisperTarget", 0);
t.set("textareaValue", text);
Ember.run.next(function() {
move_selection(t.get('chatTextArea'), 4 + wt.length);
});
}
});
} else
return this._onKeyDown(event);
break;
case KEYCODES.COLON:
if ( false && f.settings.input_emoji && (e.shiftKey || e.shiftLeft) ) {
var t = this,
ind = selection_start(this.get("chatTextArea"));
ind > 0 && Ember.run.next(function() {
var text = t.get("textareaValue"),
emoji_start = text.lastIndexOf(":", ind - 1);
if ( emoji_start !== -1 && ind !== -1 && text.charAt(ind) === ":" ) {
var match = text.substr(emoji_start + 1, ind-emoji_start - 1),
emoji_id = f.emoji_names[match],
emoji = f.emoji_data[emoji_id];
if ( emoji ) {
var prefix = text.substr(0, emoji_start) + emoji.raw;
t.set('textareaValue', prefix + text.substr(ind + 1));
Ember.run.next(function() {
move_selection(t.get('chatTextArea'), prefix.length);
});
}
}
});
return;
}
return this._onKeyDown(event);
case KEYCODES.ENTER:
if ( ! e.shiftKey && ! e.shiftLeft )
this.set('ffz_mru_index', -1);
default:
return this._onKeyDown(event);
}
},
ffzCycleMRU: function(key) {
var ind = this.get('ffz_mru_index'),
mru = this._parentView.get('context.model.mru_list') || [];
if ( key === KEYCODES.UP )
ind = (ind + 1) % (mru.length + 1);
else
ind = (ind + mru.length) % (mru.length + 1);
var old_val = this.get('ffz_old_mru');
if ( old_val === undefined )
this.set('ffz_old_mru', this.get('textareaValue'));
var new_val = mru[ind];
if ( new_val === undefined ) {
this.set('ffz_old_mru', undefined);
new_val = old_val;
}
this.set('ffz_mru_index', ind);
this.set('textareaValue', new_val);
},
completeSuggestion: function(e) {
var r, n, i = this,
o = this.get("textareaValue"),
a = this.get("partialNameStartIndex");
r = o.substring(0, a) + (o.charAt(0) === "/" ? e : FFZ.get_capitalization(e));
n = o.substring(a + this.get("partialName").length);
if ( ! n )
r += " ";
this.set("textareaValue", r + n);
this.set("isShowingSuggestions", false);
this.set("partialName", "");
this.trackSuggestionsCompleted();
Ember.run.next(function() {
move_selection(i.get('chatTextArea'), r.length);
});
}
/*ffz_emoticons: function() {
var output = [],
room = this._parentView.get('context.model'),
room_id = room && room.get('id'),
tmi = room && room.tmiSession,
user = f.get_user(),
ffz_sets = f.getEmotes(user && user.login, room_id);
if ( tmi ) {
var es = tmi.getEmotes();
if ( es && es.emoticon_sets ) {
for(var set_id in es.emoticon_sets) {
var emote_set = es.emoticon_sets[set_id];
for(var emote_id in emote_set) {
if ( emote_set[emote_id] ) {
var code = emote_set[emote_id].code;
output.push({id: constants.KNOWN_CODES[code] || code});
}
}
}
}
}
for(var i=0; i < ffz_sets.length; i++) {
var emote_set = f.emote_sets[ffz_sets[i]];
if ( ! emote_set )
continue;
for(var emote_id in emote_set.emoticons) {
var emote = emote_set.emoticons[emote_id];
if ( ! emote.hidden )
output.push({id:emote.name});
}
}
return output;
}.property(),
ffz_chatters: [],
suggestions: function(key, value, previousValue) {
if ( arguments.length > 1 ) {
this.set('ffz_chatters', value);
}
var output = [];
// Chatters
output = output.concat(this.get('ffz_chatters'));
// Emoticons
if ( this.get('isSuggestionsTriggeredWithTab') ) {
output = output.concat(this.get('ffz_emoticons'));
}
return output;
}.property("ffz_emoticons", "ffz_chatters", "isSuggestionsTriggeredWithTab")*/
});
}

View file

@ -18,11 +18,26 @@ var FFZ = window.FrankerFaceZ,
// -------------------- // --------------------
FFZ.settings_info.minimal_chat = { FFZ.settings_info.swap_sidebars = {
type: "boolean", type: "boolean",
value: false, value: false,
//no_bttv: true, category: "Miscellaneous",
no_bttv: true,
name: "Swap Sidebar Positions",
help: "Swap the positions of the left and right sidebars, placing chat on the left.",
on_update: function(val) {
if ( ! this.has_bttv )
document.body.classList.toggle("ffz-sidebar-swap", val);
}
};
FFZ.settings_info.minimal_chat = {
type: "boolean",
value: false,
category: "Chat", category: "Chat",
name: "Minimalistic Chat", name: "Minimalistic Chat",
@ -34,6 +49,7 @@ FFZ.settings_info.minimal_chat = {
var f = this; var f = this;
setTimeout(function() { setTimeout(function() {
f._chatv && f._chatv.$('.chat-room').css('top', f._chatv._ffz_tabs.offsetHeight + "px"); f._chatv && f._chatv.$('.chat-room').css('top', f._chatv._ffz_tabs.offsetHeight + "px");
f._roomv && f._roomv.get('stuckToBottom') && f._roomv._scrollToBottom();
},0); },0);
} }
} }
@ -121,8 +137,11 @@ FFZ.settings_info.pinned_rooms = {
// -------------------- // --------------------
FFZ.prototype.setup_chatview = function() { FFZ.prototype.setup_chatview = function() {
//if ( ! this.has_bttv )
document.body.classList.toggle("ffz-minimal-chat", this.settings.minimal_chat); document.body.classList.toggle("ffz-minimal-chat", this.settings.minimal_chat);
if ( ! this.has_bttv )
document.body.classList.toggle("ffz-sidebar-swap", this.settings.swap_sidebars);
this.log("Hooking the Ember Chat controller."); this.log("Hooking the Ember Chat controller.");
@ -186,7 +205,6 @@ FFZ.prototype.setup_chatview = function() {
} }
} }
this.log("Hooking the Ember Layout controller."); this.log("Hooking the Ember Layout controller.");
var Layout = App.__container__.lookup('controller:layout'); var Layout = App.__container__.lookup('controller:layout');
if ( ! Layout ) if ( ! Layout )

View file

@ -185,6 +185,32 @@
// Settings // Settings
// --------------------- // ---------------------
FFZ.settings_info.room_status = {
type: "boolean",
value: true,
category: "Chat",
no_bttv: true,
name: "Room Status Indicators",
help: "Display the current room state (slow mode, sub mode, and r9k mode) next to the Chat button.",
on_update: function() {
if ( this._roomv )
this._roomv.ffzUpdateStatus();
}
};
FFZ.settings_info.replace_bad_emotes = {
type: "boolean",
value: true,
category: "Chat",
name: "Fix Low Quality Twitch Global Emoticons",
help: "Replace emoticons such as DansGame and RedCoat with cleaned up versions that don't have pixels around the edges or white backgrounds for nicer display on dark chat."
}
FFZ.settings_info.parse_emoji = { FFZ.settings_info.parse_emoji = {
type: "boolean", type: "boolean",
value: true, value: true,
@ -600,7 +626,7 @@ FFZ.prototype._modify_line = function(component) {
if ( data ) { if ( data ) {
img.setAttribute('srcset', data.srcSet); img.setAttribute('srcset', data.srcSet);
img.title = "Emoji: " + img.alt + "\nName: " + data.short_name; img.title = "Emoji: " + img.alt + "\nName: :" + data.short_name + ":";
} }
} else if ( img.getAttribute('data-ffz-emote') ) { } else if ( img.getAttribute('data-ffz-emote') ) {

View file

@ -361,6 +361,16 @@ FFZ.prototype.setup_mod_card = function() {
} }
} }
// Reposition the menu if it's off-screen.
var el_bound = el.getBoundingClientRect(),
body_bound = document.body.getBoundingClientRect();
if ( el_bound.bottom > body_bound.bottom ) {
var offset = el_bound.bottom - body_bound.bottom;
if ( el_bound.top - offset > body_bound.top )
el.style.top = (el_bound.top - offset) + "px";
}
// Focus the Element // Focus the Element
this.$().draggable({ this.$().draggable({
start: function() { start: function() {

View file

@ -307,10 +307,10 @@ FFZ.prototype._modify_rview = function(view) {
ffzMouseOut: function(event) { ffzMouseOut: function(event) {
this._ffz_outside = true; this._ffz_outside = true;
var e = this; var e = this;
Ember.run.next(function() { setTimeout(function() {
if ( e._ffz_outside ) if ( e._ffz_outside )
e.ffzUnfreeze(); e.ffzUnfreeze();
}); }, 25);
}, },
ffzMouseMove: function(event) { ffzMouseMove: function(event) {
@ -808,6 +808,8 @@ FFZ.prototype._modify_room = function(room) {
slowWaiting: false, slowWaiting: false,
slowValue: 0, slowValue: 0,
mru_list: [],
updateWait: function(value, was_banned) { updateWait: function(value, was_banned) {
var wait = this.get('slowWait') || 0; var wait = this.get('slowWait') || 0;
this.set('slowWait', value); this.set('slowWait', value);
@ -978,6 +980,19 @@ FFZ.prototype._modify_room = function(room) {
return; return;
try { try {
if ( text ) {
// Command History
var mru = this.get('mru_list'),
ind = mru.indexOf(text);
if ( ind !== -1 )
mru.splice(ind, 1)
else if ( mru.length > 20 )
mru.pop();
mru.unshift(text);
}
var cmd = text.split(' ', 1)[0].toLowerCase(); var cmd = text.split(' ', 1)[0].toLowerCase();
if ( cmd === "/ffz" ) { if ( cmd === "/ffz" ) {
this.set("messageToSend", ""); this.set("messageToSend", "");

View file

@ -47,7 +47,19 @@ var FFZ = window.FrankerFaceZ,
}, },
build_css = build_new_css; build_css = build_new_css,
from_code_point = function(cp) {
var code = typeof cp === "string" ? parseInt(cp, 16) : cp;
if ( code < 0x10000)
return String.fromCharCode(code);
code -= 0x10000;
return String.fromCharCode(
0xD800 + (code >> 10),
0xDC00 + (code & 0x3FF)
);
};
// --------------------- // ---------------------
@ -58,6 +70,8 @@ FFZ.prototype.setup_emoticons = function() {
this.log("Preparing emoticon system."); this.log("Preparing emoticon system.");
this.emoji_data = {}; this.emoji_data = {};
this.emoji_names = {};
this.emote_sets = {}; this.emote_sets = {};
this.global_sets = []; this.global_sets = [];
this.default_sets = []; this.default_sets = [];
@ -206,11 +220,17 @@ FFZ.prototype.load_emoji_data = function(callback, tries) {
var f = this; var f = this;
jQuery.getJSON(constants.SERVER + "emoji/emoji.json") jQuery.getJSON(constants.SERVER + "emoji/emoji.json")
.done(function(data) { .done(function(data) {
var new_data = {}; var new_data = {},
by_name = {};
for(var eid in data) { for(var eid in data) {
var emoji = data[eid]; var emoji = data[eid];
eid = eid.toLowerCase(); eid = eid.toLowerCase();
emoji.code = eid;
new_data[eid] = emoji; new_data[eid] = emoji;
by_name[emoji.short_name] = eid;
emoji.raw = _.map(emoji.code.split("-"), from_code_point).join("");
emoji.src = constants.SERVER + 'emoji/' + eid + '-1x.png'; emoji.src = constants.SERVER + 'emoji/' + eid + '-1x.png';
emoji.srcSet = emoji.src + ' 1x, ' + constants.SERVER + 'emoji/' + eid + '-2x.png 2x, ' + constants.SERVER + 'emoji/' + eid + '-4x.png 4x'; emoji.srcSet = emoji.src + ' 1x, ' + constants.SERVER + 'emoji/' + eid + '-2x.png 2x, ' + constants.SERVER + 'emoji/' + eid + '-4x.png 4x';
@ -219,11 +239,14 @@ FFZ.prototype.load_emoji_data = function(callback, tries) {
srcSet: emoji.srcSet, srcSet: emoji.srcSet,
emoticonSrc: emoji.src + '" data-ffz-emoji="' + eid + '" height="18px', emoticonSrc: emoji.src + '" data-ffz-emoji="' + eid + '" height="18px',
ffzEmoji: eid, ffzEmoji: eid,
altText: emoji.raw
}; };
} }
f.emoji_data = new_data; f.emoji_data = new_data;
f.emoji_names = by_name;
f.log("Loaded data on " + Object.keys(new_data).length + " emoji."); f.log("Loaded data on " + Object.keys(new_data).length + " emoji.");
if ( typeof callback === "function" ) if ( typeof callback === "function" )
callback(true, data); callback(true, data);

View file

@ -52,6 +52,8 @@ FFZ.prototype.setup_bttv = function(delay) {
// Disable other features too. // Disable other features too.
document.body.classList.remove("ffz-chat-colors"); document.body.classList.remove("ffz-chat-colors");
document.body.classList.remove("ffz-chat-background"); document.body.classList.remove("ffz-chat-background");
document.body.classList.remove("ffz-sidebar-swap");
document.body.classList.remove("ffz-transparent-badges");
// Remove Sub Count // Remove Sub Count
if ( this.is_dashboard ) if ( this.is_dashboard )
@ -246,11 +248,10 @@ FFZ.prototype.setup_bttv = function(delay) {
bits.push(match); bits.push(match);
else { else {
var eid = utils.emoji_to_codepoint(match, variant), var eid = utils.emoji_to_codepoint(match, variant),
data = f.emoji_data[eid], data = f.emoji_data[eid];
alt = match + (variant || "");
if ( data ) { if ( data ) {
tokens.push(['<img class="emoticon" height="18px" srcset="' + (data.srcSet || "") + '" src="' + data.src + '" alt="' + alt + '" title="Emoji: ' + alt + '\nName: ' + data.short_name + '">']); tokens.push(['<img class="emoticon" height="18px" srcset="' + (data.srcSet || "") + '" src="' + data.src + '" alt="' + alt + '" title="Emoji: ' + data.raw + '\nName: :' + data.short_name + ':">']);
} else } else
tokens.push(match + (variant || "")); tokens.push(match + (variant || ""));
} }

View file

@ -21,7 +21,7 @@ FFZ.get = function() { return FFZ.instance; }
// Version // Version
var VER = FFZ.version_info = { var VER = FFZ.version_info = {
major: 3, minor: 4, revision: 10, major: 3, minor: 4, revision: 11,
toString: function() { toString: function() {
return [VER.major, VER.minor, VER.revision].join(".") + (VER.extra || ""); return [VER.major, VER.minor, VER.revision].join(".") + (VER.extra || "");
} }
@ -120,6 +120,7 @@ require('./ember/line');
require('./ember/chatview'); require('./ember/chatview');
require('./ember/viewers'); require('./ember/viewers');
require('./ember/moderation-card'); require('./ember/moderation-card');
require('./ember/chat-input');
//require('./ember/teams'); //require('./ember/teams');
// Analytics: require('./tracking'); // Analytics: require('./tracking');
@ -283,6 +284,7 @@ FFZ.prototype.setup_ember = function(delay) {
this.setup_chatview(); this.setup_chatview();
this.setup_viewers(); this.setup_viewers();
this.setup_mod_card(); this.setup_mod_card();
this.setup_chat_input();
//this.setup_teams(); //this.setup_teams();

View file

@ -62,7 +62,7 @@ FFZ.settings_info.replace_twitch_menu = {
type: "boolean", type: "boolean",
value: false, value: false,
name: "Replace Twitch Emoticon Menu <span>Beta</span>", name: "Replace Twitch Emoticon Menu",
help: "Completely replace the default Twitch emoticon menu.", help: "Completely replace the default Twitch emoticon menu.",
on_update: function(val) { on_update: function(val) {

View file

@ -53,7 +53,10 @@ FFZ.prototype.tokenize_chat_line = function(msgObject, prevent_notification) {
tokens = helpers.linkifyMessage(tokens); tokens = helpers.linkifyMessage(tokens);
if ( user && user.login ) if ( user && user.login )
tokens = helpers.mentionizeMessage(tokens, user.login, from_me); tokens = helpers.mentionizeMessage(tokens, user.login, from_me);
tokens = helpers.emoticonizeMessage(tokens, emotes); tokens = helpers.emoticonizeMessage(tokens, emotes);
if ( this.settings.replace_bad_emotes )
tokens = this.tokenize_replace_emotes(tokens);
// FrankerFaceZ Extras // FrankerFaceZ Extras
tokens = this._remove_banned(tokens); tokens = this._remove_banned(tokens);
@ -140,7 +143,7 @@ FFZ.prototype.tokenize_chat_line = function(msgObject, prevent_notification) {
} }
FFZ.prototype.tokenize_line = function(user, room, message, no_emotes) { FFZ.prototype.tokenize_line = function(user, room, message, no_emotes, no_emoji) {
if ( typeof message === "string" ) if ( typeof message === "string" )
message = [message]; message = [message];
@ -153,8 +156,14 @@ FFZ.prototype.tokenize_line = function(user, room, message, no_emotes) {
message = helpers.mentionizeMessage(message, u.login, user === u.login); message = helpers.mentionizeMessage(message, u.login, user === u.login);
} }
if ( ! no_emotes ) if ( ! no_emotes ) {
message = this.tokenize_emotes(user, room, message); message = this.tokenize_emotes(user, room, message);
if ( this.settings.replace_bad_emotes )
message = this.tokenize_replace_emotes(message);
}
if ( this.settings.parse_emoji && ! no_emoji )
message = this.tokenize_emoji(message);
return message; return message;
} }
@ -175,7 +184,7 @@ FFZ.prototype.render_tokens = function(tokens, render_links) {
var eid = token.ffzEmoji, var eid = token.ffzEmoji,
emoji = f.emoji_data && f.emoji_data[eid]; emoji = f.emoji_data && f.emoji_data[eid];
tooltip = emoji ? "Emoji: " + token.altText + "\nName: " + emoji.short_name : token.altText; tooltip = emoji ? "Emoji: " + token.altText + "\nName: :" + emoji.short_name + ":" : token.altText;
} else { } else {
var id = FFZ.src_to_id(token.emoticonSrc), var id = FFZ.src_to_id(token.emoticonSrc),
@ -218,6 +227,29 @@ FFZ.prototype.render_tokens = function(tokens, render_links) {
// Emoticon Processing // Emoticon Processing
// --------------------- // ---------------------
FFZ.prototype.tokenize_replace_emotes = function(tokens) {
// Replace bad Twitch emoticons with custom emoticons.
var f = this;
if ( _.isString(tokens) )
tokens = [tokens];
for(var i=0; i < tokens.length; i++) {
var token = tokens[i];
if ( ! token || ! token.emoticonSrc || token.ffzEmote )
continue;
// Check for a few specific emoticon IDs.
var emote_id = FFZ.src_to_id(token.emoticonSrc);
if ( constants.EMOTE_REPLACEMENTS.hasOwnProperty(emote_id) ) {
token.emoticonSrc = constants.EMOTE_REPLACEMENT_BASE + constants.EMOTE_REPLACEMENTS[emote_id] + '" data-twitch-emote="' + emote_id;
}
}
return tokens;
}
FFZ.prototype.tokenize_title_emotes = function(tokens) { FFZ.prototype.tokenize_title_emotes = function(tokens) {
var f = this, var f = this,
Channel = App.__container__.lookup('controller:channel'), Channel = App.__container__.lookup('controller:channel'),
@ -396,14 +428,12 @@ FFZ.prototype.tokenize_emoji = function(tokens) {
} else { } else {
// Find the right image~! // Find the right image~!
var eid = utils.emoji_to_codepoint(match, variant), var eid = utils.emoji_to_codepoint(match, variant),
data = f.emoji_data[eid], data = f.emoji_data[eid];
alt = match + (variant || "");
if ( data ) { if ( data )
data.token.altText = alt;
bits.push(data.token); bits.push(data.token);
} else else
bits.push(alt); bits.push(match + (variant || ""));
} }
} }
} }

View file

@ -3,31 +3,7 @@ var FFZ = window.FrankerFaceZ,
utils = require("../utils"), utils = require("../utils"),
TWITCH_BASE = "http://static-cdn.jtvnw.net/emoticons/v1/", TWITCH_BASE = "http://static-cdn.jtvnw.net/emoticons/v1/",
BANNED_SETS = {"00000turbo":true}, BANNED_SETS = {"00000turbo":true};
KNOWN_CODES = {
"#-?[\\\\/]": "#-/",
":-?(?:7|L)": ":-7",
"\\&lt\\;\\]": "<]",
"\\:-?(S|s)": ":-S",
"\\:-?\\\\": ":-\\",
"\\:\\&gt\\;": ":>",
"B-?\\)": "B-)",
"\\:-?[z|Z|\\|]": ":-Z",
"\\:-?\\)": ":-)",
"\\:-?\\(": ":-(",
"\\:-?(p|P)": ":-P",
"\\;-?(p|P)": ";-P",
"\\&lt\\;3": "<3",
"\\:-?[\\\\/]": ":-/",
"\\;-?\\)": ";-)",
"R-?\\)": "R-)",
"[o|O](_|\\.)[o|O]": "O.o",
"\\:-?D": ":-D",
"\\:-?(o|O)": ":-O",
"\\&gt\\;\\(": ">(",
"Gr(a|e)yFace": "GrayFace"
};
// ------------------- // -------------------
@ -43,6 +19,15 @@ FFZ.settings_info.global_emotes_in_menu = {
}; };
FFZ.settings_info.emoji_in_menu = {
type: "boolean",
value: false,
name: "Display Emoji in My Emotes",
help: "Display the supported emoji images in the My Emoticons menu."
};
FFZ.prototype.setup_my_emotes = function() { FFZ.prototype.setup_my_emotes = function() {
this._twitch_set_to_channel = {}; this._twitch_set_to_channel = {};
this._twitch_badges = {}; this._twitch_badges = {};
@ -129,6 +114,52 @@ FFZ.menu_pages.my_emotes = {
setTimeout(fail, 2000); setTimeout(fail, 2000);
}, },
draw_emoji: function(view) {
var heading = document.createElement('div'),
menu = document.createElement('div');
heading.className = 'heading';
heading.innerHTML = '<span class="right">FrankerFaceZ</span>Emoji';
menu.className = 'emoticon-grid';
menu.appendChild(heading);
var set = [];
for(var eid in this.emoji_data)
set.push(this.emoji_data[eid]);
set.sort(function(a,b) {
var an = a.short_name.toLowerCase(),
bn = b.short_name.toLowerCase();
if ( an < bn ) return -1;
else if ( an > bn ) return 1;
if ( a.raw < b.raw ) return -1;
if ( a.raw > b.raw ) return 1;
return 0;
});
for(var i=0; i < set.length; i++) {
var emoji = set[i],
em = document.createElement('span'),
img_set = 'image-set(url("' + emoji.src + '") 1x, url("' + constants.SERVER + 'emoji/' + emoji.code + '-2x.png") 2x, url("' + constants.SERVER + 'emoji/' + emoji.code + '-4x.png") 4x)';
em.className = 'emoticon tooltip';
em.title = 'Emoji: ' + emoji.raw + '\nName: :' + emoji.short_name + ':';
em.addEventListener('click', this._add_emote.bind(this, view, emoji.raw));
em.style.backgroundImage = 'url("' + emoji.src + '")';
em.style.backgroundImage = '-webkit-' + img_set;
em.style.backgroundImage = '-moz-' + img_set;
em.style.backgroundImage = '-ms-' + img_set;
em.style.backgroudnImage = img_set;
menu.appendChild(em);
}
return menu;
},
draw_twitch_set: function(view, set_id, set) { draw_twitch_set: function(view, set_id, set) {
var heading = document.createElement('div'), var heading = document.createElement('div'),
menu = document.createElement('div'), menu = document.createElement('div'),
@ -164,19 +195,35 @@ FFZ.menu_pages.my_emotes = {
menu.className = 'emoticon-grid'; menu.className = 'emoticon-grid';
menu.appendChild(heading); menu.appendChild(heading);
set.sort(function(a,b) {
var an = a.code.toLowerCase(),
bn = b.code.toLowerCase();
if ( an < bn ) return -1;
else if ( an > bn ) return 1;
if ( a.id < b.id ) return -1;
if ( a.id > b.id ) return 1;
return 0;
});
for(var i=0; i < set.length; i++) { for(var i=0; i < set.length; i++) {
var emote = set[i], var emote = set[i],
code = KNOWN_CODES[emote.code] || emote.code, code = constants.KNOWN_CODES[emote.code] || emote.code,
em = document.createElement('span'), em = document.createElement('span'),
img_set = 'image-set(url("' + TWITCH_BASE + emote.id + '/1.0") 1x, url("' + TWITCH_BASE + emote.id + '/2.0") 2x, url("' + TWITCH_BASE + emote.id + '/3.0") 4x)'; img_set = 'image-set(url("' + TWITCH_BASE + emote.id + '/1.0") 1x, url("' + TWITCH_BASE + emote.id + '/2.0") 2x, url("' + TWITCH_BASE + emote.id + '/3.0") 4x)';
em.className = 'emoticon tooltip'; em.className = 'emoticon tooltip';
em.style.backgroundImage = 'url("' + TWITCH_BASE + emote.id + '/1.0")';
em.style.backgroundImage = '-webkit-' + img_set; if ( this.settings.replace_bad_emotes && constants.EMOTE_REPLACEMENTS[emote.id] ) {
em.style.backgroundImage = '-moz-' + img_set; em.style.backgroundImage = 'url("' + constants.EMOTE_REPLACEMENT_BASE + constants.EMOTE_REPLACEMENTS[emote.id] + '")';
em.style.backgroundImage = '-ms-' + img_set; } else {
em.style.backgroudnImage = img_set; em.style.backgroundImage = 'url("' + TWITCH_BASE + emote.id + '/1.0")';
em.style.backgroundImage = '-webkit-' + img_set;
em.style.backgroundImage = '-moz-' + img_set;
em.style.backgroundImage = '-ms-' + img_set;
em.style.backgroudnImage = img_set;
}
em.title = code; em.title = code;
em.addEventListener("click", this._add_emote.bind(this, view, code)); em.addEventListener("click", this._add_emote.bind(this, view, code));
@ -270,6 +317,9 @@ FFZ.menu_pages.my_emotes = {
sets.push([this._twitch_set_to_channel[set_id], FFZ.menu_pages.my_emotes.draw_twitch_set.bind(this)(view, set_id, set)]); sets.push([this._twitch_set_to_channel[set_id], FFZ.menu_pages.my_emotes.draw_twitch_set.bind(this)(view, set_id, set)]);
} }
// Emoji~!
if ( this.settings.emoji_in_menu )
sets.push(["emoji", FFZ.menu_pages.my_emotes.draw_emoji.bind(this)(view)]);
// Now, FFZ! // Now, FFZ!
for(var i=0; i < ffz_sets.length; i++) { for(var i=0; i < ffz_sets.length; i++) {

100
style.css
View file

@ -1087,6 +1087,11 @@ body.ffz-minimal-chat .ember-chat .chat-interface .textarea-contain textarea {
list-style-position: unset; list-style-position: unset;
} }
.ember-chat .moderation-card .interface.chat-history .chat-line.action {
float: none;
margin-right: 0px;
}
.chat-history .chat-line.admin .message { color: #666; } .chat-history .chat-line.admin .message { color: #666; }
.chat-history .timestamp { .chat-history .timestamp {
@ -1128,4 +1133,99 @@ body.ffz-minimal-chat .ember-chat .chat-interface .textarea-contain textarea {
.ember-chat-container.force-dark .button-primary.ffz-waiting.ffz-banned:not(:hover) { .ember-chat-container.force-dark .button-primary.ffz-waiting.ffz-banned:not(:hover) {
background-color: rgba(255,128,128,0.1); background-color: rgba(255,128,128,0.1);
color: #f66; color: #f66;
}
/* Swap Sidebars */
.ffz-sidebar-swap .ember-chat .chat-interface .emoticon-selector {
right: auto;
left: 20px;
}
.ffz-sidebar-swap #left_col {
left: auto;
right: 0;
}
.ffz-sidebar-swap #right_col {
right: auto;
left: 0;
}
.ffz-sidebar-swap #right_close,
.ffz-sidebar-swap #left_close {
transform: scaleX(-1);
}
.ffz-sidebar-swap #right_close {
right: auto;
left: 5px;
}
.ffz-sidebar-swap #left_close {
right: auto;
left: -25px;
}
.ffz-sidebar-swap #main_col {
margin-left: 340px;
margin-right: 240px;
}
.ffz-sidebar-swap #main_col.expandLeft {
margin-right: 50px;
}
.ffz-sidebar-swap #main_col.expandRight {
margin-left: 0px;
}
.ffz-sidebar-swap #flyout {
left: auto !important;
right: 50px;
}
.ffz-sidebar-swap #flyout .content {
right: 10px;
}
.ffz-sidebar-swap #flyout .point {
left: auto;
right: -1px;
}
.ffz-sidebar-swap #flyout .point:before {
border-left-color: #fff;
border-right-color: transparent;
right: auto;
left: 0px;
}
.ffz-sidebar-swap #flyout .point:after {
border-right-color: transparent;
border-left-color: rgba(0,0,0,0.25);
right: auto;
left: 1px;
}
.ffz-dark.ffz-sidebar-swap #flyout .point:after {
border-left-color: #32323e;
border-right-color: transparent;
}
.ffz-dark.ffz-sidebar-swap #flyout .point:before {
border-left-color: #101010;
border-right-color: transparent;
}
/* Hidden Badges */
.ffz-transparent-badges .ember-chat .badges .badge {
background-color: transparent !important;
}
.ffz-transparent-badges .app-main:not(.theatre) .chat-container:not(.dark):not(.force-dark) .ember-chat .badges .badge:not(.ffz-badge-0):not(.subscriber),
.ffz-transparent-badges .app-main:not(.theatre) .ember-chat-container:not(.dark):not(.force-dark) .ember-chat .badges .badge:not(.ffz-badge-0):not(.subscriber) {
filter: invert(100%);
-webkit-filter: invert(100%);
} }