diff --git a/gulpfile.js b/gulpfile.js index 13128efa..baa2333e 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -105,15 +105,20 @@ gulp.task('server', function() { return request.get("http://cdn.frankerfacez.com/" + uri).pipe(res); } + var headers = {"Access-Control-Allow-Origin": "*"}; + if ( fs.lstatSync(file).isDirectory() ) { util.log("[" + util.colors.cyan("HTTP") + "] " + util.colors.red("403") + " GET " + util.colors.magenta(uri)); - res.writeHead(403, {"Access-Control-Allow-Origin": "*"}); + res.writeHead(403, headers); res.write('403 Forbidden'); return res.end(); } + if ( file.substr(file.length-4) === ".svg" ) + headers['Content-Type'] = 'image/svg+xml'; + util.log("[" + util.colors.cyan("HTTP") + "] " + util.colors.green("200") + " GET " + util.colors.magenta(uri)); - res.writeHead(200, {"Access-Control-Allow-Origin": "*"}); + res.writeHead(200, headers); fs.createReadStream(file).pipe(res); }); diff --git a/src/badges.js b/src/badges.js index ce80ada2..f418153c 100644 --- a/src/badges.js +++ b/src/badges.js @@ -17,19 +17,77 @@ FFZ.settings_info.show_badges = { }; +FFZ.settings_info.legacy_badges = { + type: "select", + options: { + 0: "Default", + 1: "Moderator Only", + 2: "Mod + Turbo", + 3: "All Legacy Badges" + }, + value: 0, + + category: "Chat Appearance", + + name: "Legacy Badges", + help: "Use the old, pre-vector chat badges from Twitch in place of the new.", + + process_value: function(val) { + if ( val === false ) + return 0; + else if ( val === true ) + return 3; + else if ( typeof val === "string" ) + return parseInt(val || "0"); + return val; + }, + + on_update: function(val) { + document.body.classList.toggle("ffz-legacy-mod-badges", val !== 0); + document.body.classList.toggle("ffz-legacy-turbo-badges", val > 1); + document.body.classList.toggle("ffz-legacy-badges", val === 3); + } + }; + + FFZ.settings_info.transparent_badges = { - type: "boolean", - value: false, + type: "select", + options: { + 0: "Default", + 1: "Rounded", + 2: "Circular", + 3: "Circular (Color Only)", + 4: "Circular (Color Only, Small)", + 5: "Transparent" + }, + + value: 0, category: "Chat Appearance", 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.", - + + name: "Badge Style", + help: "Make badges appear rounded, completely circular, or transparent with no background at all.", + + process_value: function(val) { + if ( val === false ) + return 0; + else if ( val === true ) + return 5; + else if ( typeof val === "string" ) + return parseInt(val || "0"); + return val; + }, + on_update: function(val) { - if ( ! this.has_bttv ) - document.body.classList.toggle("ffz-transparent-badges", val); + if ( this.has_bttv ) + return; + + document.body.classList.toggle("ffz-rounded-badges", val === 1); + document.body.classList.toggle("ffz-circular-badges", val === 2); + document.body.classList.toggle("ffz-circular-blank-badges", val === 3); + document.body.classList.toggle("ffz-circular-small-badges", val === 4); + document.body.classList.toggle("ffz-transparent-badges", val === 5); } }; @@ -39,9 +97,18 @@ FFZ.settings_info.transparent_badges = { // -------------------- FFZ.prototype.setup_badges = function() { - if ( ! this.has_bttv ) - document.body.classList.toggle("ffz-transparent-badges", this.settings.transparent_badges); - + if ( ! this.has_bttv ) { + document.body.classList.toggle("ffz-rounded-badges", this.settings.transparent_badges === 1); + document.body.classList.toggle("ffz-circular-badges", this.settings.transparent_badges === 2); + document.body.classList.toggle("ffz-circular-blank-badges", this.settings.transparent_badges === 3); + document.body.classList.toggle("ffz-circular-small-badges", this.settings.transparent_badges === 4); + document.body.classList.toggle("ffz-transparent-badges", this.settings.transparent_badges === 5); + } + + document.body.classList.toggle("ffz-legacy-mod-badges", this.settings.legacy_badges !== 0); + document.body.classList.toggle("ffz-legacy-turbo-badges", this.settings.legacy_badges > 1); + document.body.classList.toggle("ffz-legacy-badges", this.settings.legacy_badges === 3); + this.log("Preparing badge system."); this.badges = {}; @@ -206,7 +273,7 @@ FFZ.prototype.render_badges = function(component, badges) { var visible = full_badge.visible; if ( typeof visible === "function" ) visible = visible.bind(this)(room_id, user, component, badges); - + if ( ! visible ) continue; } @@ -215,7 +282,7 @@ FFZ.prototype.render_badges = function(component, badges) { var replaces = badge.hasOwnProperty('replaces') ? badge.replaces : full_badge.replaces; if ( ! replaces ) continue; - + old_badge.image = badge.image || full_badge.image; old_badge.klass += ' ffz-badge-replacement'; old_badge.title += ', ' + (badge.title || full_badge.title); @@ -230,7 +297,7 @@ FFZ.prototype.render_badges = function(component, badges) { extra_css: badge.extra_css }; } - + return badges; } diff --git a/src/ember/line.js b/src/ember/line.js index 0e55d8b1..3c660534 100644 --- a/src/ember/line.js +++ b/src/ember/line.js @@ -59,13 +59,29 @@ FFZ.settings_info.replace_bad_emotes = { FFZ.settings_info.parse_emoji = { - type: "boolean", - value: true, + type: "select", + options: { + 0: "No Images / Font Only", + 1: "Twitter Emoji Images", + 2: "Google Noto Images" + }, + + value: 1, + + process_value: function(val) { + if ( val === false ) + return 0; + if ( val === true ) + return 1; + if ( typeof val === "string" ) + return parseInt(val || "0"); + return val; + }, category: "Chat Appearance", - name: "Replace Emoji with Images", - help: "Replace emoji in chat messages with nicer looking images from the open-source Twitter Emoji project." + name: "Emoji Display", + help: "Replace emoji in chat messages with nicer looking images from either Twitter or Google." }; @@ -262,19 +278,6 @@ FFZ.settings_info.image_hover_all_domains = { }; -FFZ.settings_info.legacy_badges = { - type: "boolean", - value: false, - - category: "Chat Appearance", - - name: "Legacy Badges", - help: "Display the old, pre-vector chat badges from Twitch.", - - on_update: function(val) { document.body.classList.toggle("ffz-legacy-badges", val); } - }; - - FFZ.settings_info.chat_rows = { type: "boolean", value: false, @@ -369,6 +372,52 @@ FFZ.settings_info.high_contrast_chat = { }; +FFZ.settings_info.chat_font_family = { + type: "button", + value: null, + + category: "Chat Appearance", + no_bttv: true, + + name: "Font Family", + help: "Change the font used for rendering chat messages.", + + method: function() { + var old_val = this.settings.chat_font_family || "", + new_val = prompt("Chat Font Family\n\nPlease enter a font family to use rendering chat. Leave this blank to use the default.", old_val); + + if ( new_val === null || new_val === undefined ) + return; + + // Should we wrap this with quotes? + if ( ! new_val ) + new_val = null; + + this.settings.set("chat_font_family", new_val); + }, + + on_update: function(val) { + if ( this.has_bttv || ! this._chat_style ) + return; + + var css; + if ( ! val ) + css = ""; + else { + // Let's escape this to avoid goofing anything up if there's bad user input. + if ( val.indexOf(' ') !== -1 && val.indexOf(',') === -1 && val.indexOf('"') === -1 && val.indexOf("'") === -1) + val = '"' + val + '"'; + + var span = document.createElement('span'); + span.style.fontFamily = val; + css = ".ember-chat .chat-messages {" + span.style.cssText + "}"; + } + + utils.update_css(this._chat_style, "chat_font_family", css); + } + }; + + FFZ.settings_info.chat_font_size = { type: "button", value: 12, @@ -486,13 +535,13 @@ FFZ.prototype.setup_line = function() { // Initial calculation. FFZ.settings_info.chat_font_size.on_update.bind(this)(this.settings.chat_font_size); + FFZ.settings_info.chat_font_family.on_update.bind(this)(this.settings.chat_font_family); // Chat Enhancements document.body.classList.toggle("ffz-chat-colors", !this.has_bttv && this.settings.fix_color !== '-1'); document.body.classList.toggle("ffz-chat-colors-gray", !this.has_bttv && this.settings.fix_color === '-1'); - document.body.classList.toggle("ffz-legacy-badges", this.settings.legacy_badges); document.body.classList.toggle('ffz-chat-background', !this.has_bttv && this.settings.chat_rows); document.body.classList.toggle("ffz-chat-separator", !this.has_bttv && this.settings.chat_separators !== '0'); document.body.classList.toggle("ffz-chat-separator-3d", !this.has_bttv && this.settings.chat_separators === '2'); @@ -652,16 +701,11 @@ FFZ.prototype._modify_line = function(component) { this_ul = this.get('ffzUserLevel'), other_ul = room && room.room && room.room.get('ffzUserLevel') || 0, - row_type = this.get('msgObject.ffz_alternate'), raw_color = this.get('msgObject.color'), colors = raw_color && f._handle_color(raw_color), is_dark = (Layout && Layout.get('isTheatreMode')) || (Settings && Settings.get('model.darkMode')); - if ( row_type === undefined ) { - row_type = f._last_row[room_id] = f._last_row.hasOwnProperty(room_id) ? !f._last_row[room_id] : false; - this.set("msgObject.ffz_alternate", row_type); - } e.push('
'); e.push(' '); diff --git a/src/ember/room.js b/src/ember/room.js index 39341dad..9f06fa6a 100644 --- a/src/ember/room.js +++ b/src/ember/room.js @@ -941,7 +941,7 @@ FFZ.prototype._modify_room = function(room) { if ( msg.from === user ) { if ( f.settings.remove_deleted ) { if ( alternate === undefined ) - alternate = msg.ffz_alternate; + alternate = ! msg.ffz_alternate; msgs.removeAt(i); continue; } @@ -1032,6 +1032,13 @@ FFZ.prototype._modify_room = function(room) { ffzActualPushMessage: function (msg) { if ( this.shouldShowMessage(msg) && this.ffzShouldShowMessage(msg) ) { + var row_type = msg.ffz_alternate; + if ( row_type === undefined ) { + var room_id = this.get('id'); + row_type = f._last_row[room_id] = f._last_row.hasOwnProperty(room_id) ? !f._last_row[room_id] : false; + msg.ffz_alternate = row_type; + } + this.get("messages").pushObject(msg); this.trimMessages(); @@ -1233,16 +1240,21 @@ FFZ.prototype._modify_room = function(room) { this.tmiRoom.list().done(function(data) { var chatters = {}; data = data.data.chatters; - for(var i=0; i < data.admins.length; i++) - chatters[data.admins[i]] = true; - for(var i=0; i < data.global_mods.length; i++) - chatters[data.global_mods[i]] = true; - for(var i=0; i < data.moderators.length; i++) - chatters[data.moderators[i]] = true; - for(var i=0; i < data.staff.length; i++) - chatters[data.staff[i]] = true; - for(var i=0; i < data.viewers.length; i++) - chatters[data.viewers[i]] = true; + if ( data && data.admins ) + for(var i=0; i < data.admins.length; i++) + chatters[data.admins[i]] = true; + if ( data && data.global_mods ) + for(var i=0; i < data.global_mods.length; i++) + chatters[data.global_mods[i]] = true; + if ( data && data.moderators ) + for(var i=0; i < data.moderators.length; i++) + chatters[data.moderators[i]] = true; + if ( data && data.staff ) + for(var i=0; i < data.staff.length; i++) + chatters[data.staff[i]] = true; + if ( data && data.viewers ) + for(var i=0; i < data.viewers.length; i++) + chatters[data.viewers[i]] = true; room.set("ffz_chatters", chatters); room.ffzUpdateChatters(); diff --git a/src/emoticons.js b/src/emoticons.js index 4b44c926..d22b3f24 100644 --- a/src/emoticons.js +++ b/src/emoticons.js @@ -218,7 +218,7 @@ FFZ.prototype._emote_tooltip = function(emote) { FFZ.prototype.load_emoji_data = function(callback, tries) { var f = this; - jQuery.getJSON(constants.SERVER + "emoji/emoji.json") + jQuery.getJSON(constants.SERVER + "emoji/emoji-data.json") .done(function(data) { var new_data = {}, by_name = {}; @@ -226,27 +226,32 @@ FFZ.prototype.load_emoji_data = function(callback, tries) { var emoji = data[eid]; eid = eid.toLowerCase(); emoji.code = eid; - + 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.srcSet = emoji.src + ' 1x, ' + constants.SERVER + 'emoji/' + eid + '-2x.png 2x, ' + constants.SERVER + 'emoji/' + eid + '-4x.png 4x'; + emoji.tw_src = constants.SERVER + 'emoji/tw-' + eid + '.svg'; + emoji.noto_src = constants.SERVER + 'emoji/noto-' + eid + '.svg'; emoji.token = { - srcSet: emoji.srcSet, - emoticonSrc: emoji.src, + emoticonSrc: true, + + tw_src: emoji.tw_src, + noto_src: emoji.noto_src, + + tw: emoji.tw, + noto: emoji.noto, + ffzEmoji: eid, altText: emoji.raw - }; - + }; } f.emoji_data = new_data; f.emoji_names = by_name; - + f.log("Loaded data on " + Object.keys(new_data).length + " emoji."); if ( typeof callback === "function" ) callback(true, data); diff --git a/src/main.js b/src/main.js index e66e5a57..159cc75a 100644 --- a/src/main.js +++ b/src/main.js @@ -21,7 +21,7 @@ FFZ.get = function() { return FFZ.instance; } // Version var VER = FFZ.version_info = { - major: 3, minor: 5, revision: 16, + major: 3, minor: 5, revision: 21, toString: function() { return [VER.major, VER.minor, VER.revision].join(".") + (VER.extra || ""); } diff --git a/src/socket.js b/src/socket.js index 43c55ca8..6263c280 100644 --- a/src/socket.js +++ b/src/socket.js @@ -176,7 +176,7 @@ FFZ.prototype.ws_create = function() { } catch(err) { f.error("Callback for " + request + ": " + err); } - + f._ws_callbacks[request] = undefined; } } diff --git a/src/tokenize.js b/src/tokenize.js index d6130513..0e5a4ce9 100644 --- a/src/tokenize.js +++ b/src/tokenize.js @@ -258,6 +258,18 @@ FFZ.settings_info.twenty_four_timestamps = { }; +FFZ.settings_info.timestamp_seconds = { + type: "boolean", + value: false, + + category: "Chat Appearance", + no_bttv: true, + + name: "Timestamp Seconds", + help: "Display seconds in chat timestamps." + }; + + FFZ.settings_info.show_deleted_links = { type: "boolean", value: false, @@ -297,14 +309,15 @@ FFZ.prototype.setup_tokenization = function() { return '?:??'; var hours = e.getHours(), - minutes = e.getMinutes(); + minutes = e.getMinutes(), + seconds = e.getSeconds(); if ( hours > 12 && ! f.settings.twenty_four_timestamps ) hours -= 12; else if ( hours === 0 && ! f.settings.twenty_four_timestamps ) hours = 12; - return hours + ':' + (minutes < 10 ? '0' : '') + minutes; + return hours + ':' + (minutes < 10 ? '0' : '') + minutes + (f.settings.timestamp_seconds ? ':' + (seconds < 10 ? '0' : '') + seconds : ''); }; @@ -523,7 +536,7 @@ FFZ.prototype.render_tokens = function(tokens, render_links) { var f = this; return _.map(tokens, function(token) { if ( token.emoticonSrc ) { - var tooltip, srcset, extra; + var tooltip, src = token.emoticonSrc, srcset, extra; if ( token.ffzEmote ) { var emote_set = f.emote_sets && f.emote_sets[token.ffzEmoteSet], emote = emote_set && emote_set.emoticons && emote_set.emoticons[token.ffzEmote]; @@ -534,11 +547,15 @@ FFZ.prototype.render_tokens = function(tokens, render_links) { } else if ( token.ffzEmoji ) { var eid = token.ffzEmoji, - emoji = f.emoji_data && f.emoji_data[eid]; + emoji = f.emoji_data && f.emoji_data[eid], + setting = f.settings.parse_emoji; - tooltip = emoji ? "Emoji: " + token.altText + "\nName: :" + emoji.short_name + ":" : token.altText; - srcset = emoji ? emoji.srcSet : token.srcSet; - extra = ' data-ffz-emoji="' + eid + '"'; + if ( setting === 0 || (setting === 1 && ! emoji.tw) || (setting === 2 && ! emoji.noto) ) + return token.altText; + + tooltip = emoji ? "Emoji: " + token.altText + "\nName: " + emoji.name + (emoji.short_name ? "\nShort Name: :" + emoji.short_name + ":" : "") : token.altText; + extra = ' data-ffz-emoji="' + eid + '" height="18px"'; + src = setting === 2 ? token.noto_src : token.tw_src; } else { var id = token.replacedId || FFZ.src_to_id(token.emoticonSrc), @@ -571,7 +588,7 @@ FFZ.prototype.render_tokens = function(tokens, render_links) { srcset = build_srcset(id); } - return '