From 7708caebb5191da6a6291e4b383191ab5d4f6b61 Mon Sep 17 00:00:00 2001 From: SirStendec Date: Thu, 16 Nov 2017 15:54:58 -0500 Subject: [PATCH] Clean up some code with eslint. Add eslintrc. Fix the FFZ:AP fix for jQuery only existing on some pages. Start renaming functions toCamelCase. Add old style RGB Loop color processing. Clean up Chat Freeze a bit. --- .eslintrc.js | 93 +++++++++++ src/api.js | 41 +++-- src/main.js | 5 +- src/modules/chat/emotes.js | 39 +++-- src/modules/chat/index.js | 7 +- src/modules/chat/room.js | 6 +- src/modules/chat/tokenizers.js | 4 +- src/settings/index.js | 14 +- .../twitch-twilight/modules/chat/index.js | 60 +++---- .../twitch-twilight/modules/chat/scroller.js | 84 +++++++--- src/utilities/color.js | 151 ++++++++++-------- 11 files changed, 333 insertions(+), 171 deletions(-) create mode 100644 .eslintrc.js diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..b964cd5f --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,93 @@ +module.exports = { + "env": { + "browser": true, + "es6": true + }, + "extends": "eslint:recommended", + "parserOptions": { + "ecmaVersion": 8, + "sourceType": "module" + }, + "globals": { + "import": false, + "require": false, + "__webpack_hash__": false + }, + "rules": { + "accessor-pairs": ["error"], + "block-scoped-var": ["error"], + "class-methods-use-this": ["error"], + "for-direction": ["error"], + "guard-for-in": ["warn"], + "no-alert": ["error"], + "no-await-in-loop": ["error"], + "no-caller": ["error"], + "no-catch-shadow": ["error"], + "no-invalid-this": ["error"], + "no-iterator": ["error"], + "no-labels": ["error"], + "no-lone-blocks": ["error"], + "no-octal-escape": ["error"], + "no-proto": ["warn"], + "no-return-await": ["error"], + "no-self-compare": ["error"], + "no-sequences": ["error"], + "no-shadow-restricted-names": ["error"], + "no-template-curly-in-string": ["warn"], + "no-throw-literal": ["error"], + "no-undef-init": ["error"], + "no-unmodified-loop-condition": ["error"], + "no-use-before-define": ["error", { + "functions": false, + "classes": false + }], + "no-useless-call": ["warn"], + "no-useless-concat": ["warn"], + "no-useless-return": ["warn"], + "no-void": ["error"], + "no-warning-comments": ["warn"], + "no-with": ["error"], + "radix": ["error"], + "require-await": ["warn"], + "valid-jsdoc": ["warn"], + "yoda": ["warn"], + + "arrow-body-style": ["warn", "as-needed"], + "arrow-parens": ["warn", "as-needed"], + "arrow-spacing": ["warn"], + "generator-star-spacing": ["warn"], + "no-duplicate-imports": ["error"], + "no-useless-computed-key": ["error"], + "no-useless-constructor": ["error"], + "no-useless-rename": ["error"], + "no-var": ["error"], + "object-shorthand": ["warn"], + "prefer-arrow-callback": ["warn", {"allowUnboundThis": true}], + "prefer-const": ["warn", {"ignoreReadBeforeAssign": true}], + "prefer-rest-params": ["warn"], + "prefer-spread": ["error"], + "prefer-template": ["warn"], + "rest-spread-spacing": ["error", "never"], + "yield-star-spacing": ["warn"], + + "indent": [ + "error", + "tab", + { + "SwitchCase": 1 + } + ], + "linebreak-style": [ + "error", + "unix" + ], + "quotes": [ + "error", + "single", + { + "avoidEscape": true, + "allowTemplateLiterals": true + } + ] + } +}; \ No newline at end of file diff --git a/src/api.js b/src/api.js index 69c5c4ab..cb3ae2b1 100644 --- a/src/api.js +++ b/src/api.js @@ -20,12 +20,12 @@ export default class ApiModule extends Module { if ( ! this._known_apis ) { this._known_apis = {}; - var stored_val = localStorage.getItem("ffz_known_apis"); + const stored_val = localStorage.getItem(`ffz_known_apis`); if ( stored_val !== null ) try { this._known_apis = JSON.parse(stored_val); } catch(err) { - this.error("Error loading Known APIs", err); + this.log.error(`Error loading known APIs`, err); } } } @@ -78,7 +78,7 @@ export class LegacyAPI extends EventEmitter { this.users = {}; this.name = name || `Extension#${this.id}`; - this.name_key = name_key || this.name.replace(/[^A-Z0-9_\-]/g, '').toLowerCase(); + this.name_key = name_key || this.name.replace(/[^A-Z0-9_-]/g, '').toLowerCase(); if ( /^[0-9]/.test(this.name_key) ) this.name_key = `_${this.name_key}`; @@ -97,9 +97,9 @@ export class LegacyAPI extends EventEmitter { this.parent.log.error(`Ext #${this.id} (${this.name_key}): ${msg}`, error); } - register_metadata(key, data) { } - unregister_metadata(key, data) { } - update_metadata(key, full_update) { } + register_metadata(key, data) { } // eslint-disable-line + unregister_metadata(key, data) { } // eslint-disable-line + update_metadata(key, full_update) { } // eslint-disable-line _load_set(real_id, set_id, data) { @@ -119,7 +119,7 @@ export class LegacyAPI extends EventEmitter { }); this.emote_sets[set_id] = emote_set; - this.parent.emotes.load_set_data(real_id, emote_set); + this.parent.emotes.loadSetData(real_id, emote_set); return emote_set; } @@ -232,23 +232,23 @@ export class LegacyAPI extends EventEmitter { } - add_badge() { } - remove_badge() { } - user_add_badge() { } - user_remove_badge() { } - room_add_user_badge() { } - room_remove_user_badge() { } + add_badge() { } // eslint-disable-line + remove_badge() { } // eslint-disable-line + user_add_badge() { } // eslint-disable-line + user_remove_badge() { } // eslint-disable-line + room_add_user_badge() { } // eslint-disable-line + room_remove_user_badge() { } // eslint-disable-line - user_add_set(username, set_id) { + user_add_set(username, set_id) { // eslint-disable-line } - user_remove_set(username, set_id) { + user_remove_set(username, set_id) { // eslint-disable-line } - retokenize_messages() { } + retokenize_messages() { } // eslint-disable-line register_chat_filter(filter) { @@ -260,20 +260,19 @@ export class LegacyAPI extends EventEmitter { } - iterate_chat_views(func) { } + iterate_chat_views(func) { } // eslint-disable-line iterate_rooms(func) { if ( func === undefined ) func = this.emit.bind(this, 'room-add'); const chat = this.parent.resolve('chat'); for(const room_id in chat.rooms) - func(room_id); + if ( has(chat.rooms, room_id) ) + func(room_id); } register_on_room_callback(callback, dont_iterate) { - const thing = room_id => { - return callback(room_id, this.register_room_set.bind(this, room_id)); - } + const thing = room_id => callback(room_id, this.register_room_set.bind(this, room_id)); thing.original_func = callback; callback.__wrapped = thing; diff --git a/src/main.js b/src/main.js index 6913aa32..1731e31b 100644 --- a/src/main.js +++ b/src/main.js @@ -126,7 +126,8 @@ window.ffz = new FrankerFaceZ(); FrankerFaceZ.chat_commands = {}; FrankerFaceZ.settings_info = {}; FrankerFaceZ.utils = { - process_int(a,b,c) { return a } + process_int: a => a } window.App = true; -jQuery.noty = {themes: {}}; \ No newline at end of file +if ( window.jQuery ) + window.jQuery.noty = {themes: {}}; \ No newline at end of file diff --git a/src/modules/chat/emotes.js b/src/modules/chat/emotes.js index 27a42d07..344a8555 100644 --- a/src/modules/chat/emotes.js +++ b/src/modules/chat/emotes.js @@ -69,7 +69,7 @@ export default class Emotes extends Module { onEnable() { // Just in case there's a weird load order going on. - this.on('site:enabled', this.refresh_twitch_inventory); + this.on('site:enabled', this.loadTwitchInventory); this.style = new ManagedStyle('emotes'); @@ -85,9 +85,9 @@ export default class Emotes extends Module { } } - this.load_global_sets(); - this.load_emoji_data(); - this.refresh_twitch_inventory(); + this.loadGlobalSets(); + this.loadEmojiData(); + this.loadTwitchInventory(); } @@ -114,14 +114,14 @@ export default class Emotes extends Module { // FFZ Emote Sets // ======================================================================== - async load_global_sets(tries = 0) { + async loadGlobalSets(tries = 0) { let response, data; try { response = await fetch(`${API_SERVER}/v1/set/global`) } catch(err) { tries++; if ( tries < 10 ) - return setTimeout(() => this.load_global_sets(tries), 500 * tries); + return setTimeout(() => this.loadGlobalSets(tries), 500 * tries); this.log.error('Error loading global emote sets.', err); return false; @@ -144,15 +144,15 @@ export default class Emotes extends Module { for(const set_id in sets) if ( has(sets, set_id) ) { this.global_sets.push('ffz-global', set_id); - this.load_set_data(set_id, sets[set_id]); + this.loadSetData(set_id, sets[set_id]); } if ( data.users ) - this.load_set_users(data.users); + this.loadSetUsers(data.users); } - load_set_users(data) { + loadSetUsers(data) { for(const set_id in data) if ( has(data, set_id) ) { const emote_set = this.emote_sets[set_id], @@ -170,7 +170,7 @@ export default class Emotes extends Module { } - load_set_data(set_id, data) { + loadSetData(set_id, data) { const old_set = this.emote_sets[set_id]; if ( ! data ) { if ( old_set ) @@ -271,8 +271,8 @@ export default class Emotes extends Module { // Emoji // ======================================================================== - load_emoji_data() { - this.log.debug('Unimplemented: load_emoji_data'); + loadEmojiData() { + this.log.debug('Unimplemented: loadEmojiData'); } @@ -280,7 +280,7 @@ export default class Emotes extends Module { // Twitch Data Lookup // ======================================================================== - async refresh_twitch_inventory() { + async loadTwitchInventory() { const user = this.resolve('site').getUser(); if ( ! user ) return; @@ -292,19 +292,26 @@ export default class Emotes extends Module { 'Client-ID': CLIENT_ID, 'Authorization': `OAuth ${user.authToken}` } - }).then(r => r.json()); + }).then(r => { + if ( r.ok ) + return r.json(); + + throw r.status; + }); } catch(err) { this.log.error('Error loading Twitch inventory.', err); return; } + + this.twitch_inventory_sets = data.emoticon_sets ? Object.keys(data.emoticon_sets) : []; this.log.info('Twitch Inventory Sets:', this.twitch_inventory_sets); } - twitch_emote_to_set(emote_id, callback) { + getTwitchEmoteSet(emote_id, callback) { const tes = this.__twitch_emote_to_set; if ( isNaN(emote_id) || ! isFinite(emote_id) ) @@ -326,7 +333,7 @@ export default class Emotes extends Module { } - twitch_set_to_channel(set_id, callback) { + getTwitchSetChannel(set_id, callback) { const tes = this.__twitch_set_to_channel; if ( isNaN(set_id) || ! isFinite(set_id) ) return null; diff --git a/src/modules/chat/index.js b/src/modules/chat/index.js index 76e73914..ad418b07 100644 --- a/src/modules/chat/index.js +++ b/src/modules/chat/index.js @@ -156,7 +156,8 @@ export default class Chat extends Module { {value: 0, title: 'Unchanged'}, {value: 1, title: 'HSL Luma'}, {value: 2, title: 'Luv Luma'}, - {value: 3, title: 'HSL Loop (BTTV-Like)'} + {value: 3, title: 'HSL Loop (BTTV-Like)'}, + {value: 4, title: 'RGB Loop (Deprecated)'} ] } }); @@ -491,8 +492,8 @@ export default class Chat extends Module { // ==== get_link_info(url, no_promises) { - let info = this._link_info[url], - expires = info && info[1]; + let info = this._link_info[url]; + const expires = info && info[1]; if ( expires && Date.now() > expires ) info = this._link_info[url] = null; diff --git a/src/modules/chat/room.js b/src/modules/chat/room.js index 7d7a659d..f441c655 100644 --- a/src/modules/chat/room.js +++ b/src/modules/chat/room.js @@ -7,7 +7,7 @@ import {API_SERVER, IS_WEBKIT} from 'utilities/constants'; import {EventEmitter} from 'utilities/events'; -import {createElement as e, ManagedStyle} from 'utilities/dom'; +import {ManagedStyle} from 'utilities/dom'; import {has, SourcedSet} from 'utilities/object'; const WEBKIT = IS_WEBKIT ? '-webkit-' : ''; @@ -126,7 +126,7 @@ export default class Room extends EventEmitter { } const d = data.room, - id = '' + d.twitch_id; + id = `${d.twitch_id}`; if ( ! this._id ) { this._id = id; @@ -149,7 +149,7 @@ export default class Room extends EventEmitter { if ( data.sets ) for(const set_id in data.sets) if ( has(data.sets, set_id) ) - this.manager.emotes.load_set_data(set_id, data.sets[set_id]); + this.manager.emotes.loadSetData(set_id, data.sets[set_id]); // TODO: User data. diff --git a/src/modules/chat/tokenizers.js b/src/modules/chat/tokenizers.js index fb5359ec..eb9e7c22 100644 --- a/src/modules/chat/tokenizers.js +++ b/src/modules/chat/tokenizers.js @@ -462,8 +462,8 @@ export const AddonEmotes = { if ( provider === 'twitch' ) { const emote_id = parseInt(target.dataset.id, 10), - set_id = this.emotes.twitch_emote_to_set(emote_id, tip.rerender), - emote_set = set_id != null && this.emotes.twitch_set_to_channel(set_id, tip.rerender); + set_id = this.emotes.getTwitchEmoteSet(emote_id, tip.rerender), + emote_set = set_id != null && this.emotes.getTwitchSetChannel(set_id, tip.rerender); preview = `//static-cdn.jtvnw.net/emoticons/v1/${emote_id}/4.0?_=preview`; diff --git a/src/settings/index.js b/src/settings/index.js index 09f20edd..f7add32d 100644 --- a/src/settings/index.js +++ b/src/settings/index.js @@ -168,18 +168,18 @@ export default class SettingsManager extends Module { // list rather than just getting the keys from the ID map // because the ID map is an object and coerces its strings // to keys. - old_ids = new Set(old_profiles.map(x => x.id)); + old_ids = new Set(old_profiles.map(x => x.id)), - let changed = false, moved_ids = new Set, new_ids = new Set, - changed_ids = new Set; + changed_ids = new Set, - const raw_profiles = this.provider.get('profiles', [ - SettingsProfile.Moderation, - SettingsProfile.Default - ]); + raw_profiles = this.provider.get('profiles', [ + SettingsProfile.Moderation, + SettingsProfile.Default + ]); + let changed = false; for(const profile_data of raw_profiles) { const id = profile_data.id, old_profile = old_profile_ids[id], diff --git a/src/sites/twitch-twilight/modules/chat/index.js b/src/sites/twitch-twilight/modules/chat/index.js index f0d9ad19..eb4a3060 100644 --- a/src/sites/twitch-twilight/modules/chat/index.js +++ b/src/sites/twitch-twilight/modules/chat/index.js @@ -17,35 +17,35 @@ import SettingsMenu from './settings_menu'; const ChatTypes = (e => { - /*e[e.Post = 0] = "Post", - e[e.Action = 1] = "Action", - e[e.PostWithMention = 2] = "PostWithMention", - e[e.Ban = 3] = "Ban", - e[e.Timeout = 4] = "Timeout", - e[e.AutoModRejectedPrompt = 5] = "AutoModRejectedPrompt", - e[e.AutoModMessageRejected = 6] = "AutoModMessageRejected", - e[e.AutoModMessageAllowed = 7] = "AutoModMessageAllowed", - e[e.AutoModMessageDenied = 8] = "AutoModMessageDenied", - e[e.Connected = 9] = "Connected", - e[e.Disconnected = 10] = "Disconnected",*/ - e[e.Reconnect = 11] = "Reconnect"; - /*e[e.Hosting = 12] = "Hosting", - e[e.Unhost = 13] = "Unhost", - e[e.Subscription = 14] = "Subscription", - e[e.Resubscription = 15] = "Resubscription", - e[e.SubGift = 16] = "SubGift", - e[e.Clear = 17] = "Clear", - e[e.SubscriberOnlyMode = 18] = "SubscriberOnlyMode", - e[e.FollowerOnlyMode = 19] = "FollowerOnlyMode", - e[e.SlowMode = 20] = "SlowMode", - e[e.RoomMods = 21] = "RoomMods",*/ - e[e.RoomState = 22] = "RoomState"; - /*e[e.Raid = 23] = "Raid", - e[e.Unraid = 24] = "Unraid", - e[e.Notice = 25] = "Notice", - e[e.Info = 26] = "Info",*/ - e[e.BadgesUpdated = 27] = "BadgesUpdated"; - //e[e.Purchase = 28] = "Purchase"; + /*e[e.Post = 0] = 'Post'; + e[e.Action = 1] = 'Action'; + e[e.PostWithMention = 2] = 'PostWithMention'; + e[e.Ban = 3] = 'Ban'; + e[e.Timeout = 4] = 'Timeout'; + e[e.AutoModRejectedPrompt = 5] = 'AutoModRejectedPrompt'; + e[e.AutoModMessageRejected = 6] = 'AutoModMessageRejected'; + e[e.AutoModMessageAllowed = 7] = 'AutoModMessageAllowed'; + e[e.AutoModMessageDenied = 8] = 'AutoModMessageDenied'; + e[e.Connected = 9] = 'Connected'; + e[e.Disconnected = 10] = 'Disconnected';*/ + e[e.Reconnect = 11] = 'Reconnect'; + /*e[e.Hosting = 12] = 'Hosting'; + e[e.Unhost = 13] = 'Unhost'; + e[e.Subscription = 14] = 'Subscription'; + e[e.Resubscription = 15] = 'Resubscription'; + e[e.SubGift = 16] = 'SubGift'; + e[e.Clear = 17] = 'Clear'; + e[e.SubscriberOnlyMode = 18] = 'SubscriberOnlyMode'; + e[e.FollowerOnlyMode = 19] = 'FollowerOnlyMode'; + e[e.SlowMode = 20] = 'SlowMode'; + e[e.RoomMods = 21] = 'RoomMods';*/ + e[e.RoomState = 22] = 'RoomState'; + /*e[e.Raid = 23] = 'Raid'; + e[e.Unraid = 24] = 'Unraid'; + e[e.Notice = 25] = 'Notice'; + e[e.Info = 26] = 'Info';*/ + e[e.BadgesUpdated = 27] = 'BadgesUpdated'; + //e[e.Purchase = 28] = 'Purchase'; return e; })({}); @@ -374,7 +374,7 @@ export default class ChatHook extends Module { this.buffer = buf.slice(removed % 2 === 0 ? target : Math.max(target - 10, last)); } else // Make a shallow copy of the array because other code expects it to change. - this.buffer = buf.slice(target); + this.buffer = buf.slice(0); this._isDirty = false; return this.buffer; diff --git a/src/sites/twitch-twilight/modules/chat/scroller.js b/src/sites/twitch-twilight/modules/chat/scroller.js index 928c532a..25c277ee 100644 --- a/src/sites/twitch-twilight/modules/chat/scroller.js +++ b/src/sites/twitch-twilight/modules/chat/scroller.js @@ -52,7 +52,6 @@ export default class Scroller extends Module { this.freeze = this.chat.context.get('chat.scroller.freeze'); this.chat.context.on('changed:chat.scroller.freeze', val => { - const old_freeze = this.freeze; this.freeze = val; for(const inst of this.ChatScroller.instances) { @@ -101,13 +100,13 @@ export default class Scroller extends Module { const f = t.freeze, reason = f === 2 ? t.i18n.t('key.ctrl', 'Ctrl Key') : f === 3 ? t.i18n.t('key.meta', 'Meta Key') : - f === 4 ? t.i18n.t('key.alt', 'Alt Key') : - f === 5 ? t.i18n.t('key.shift', 'Shift Key') : - f === 6 ? t.i18n.t('key.ctrl_mouse', 'Ctrl or Mouse') : - f === 7 ? t.i18n.t('key.meta_mouse', 'Meta or Mouse') : - f === 8 ? t.i18n.t('key.alt_mouse', 'Alt or Mouse') : - f === 9 ? t.i18n.t('key.shift_mouse', 'Shift or Mouse') : - t.i18n.t('key.mouse', 'Mouse Movement'); + f === 4 ? t.i18n.t('key.alt', 'Alt Key') : + f === 5 ? t.i18n.t('key.shift', 'Shift Key') : + f === 6 ? t.i18n.t('key.ctrl_mouse', 'Ctrl or Mouse') : + f === 7 ? t.i18n.t('key.meta_mouse', 'Meta or Mouse') : + f === 8 ? t.i18n.t('key.alt_mouse', 'Alt or Mouse') : + f === 9 ? t.i18n.t('key.shift_mouse', 'Shift or Mouse') : + t.i18n.t('key.mouse', 'Mouse Movement'); this._ffz_freeze_indicator.firstElementChild.textContent = t.i18n.t( 'chat.paused', @@ -117,6 +116,7 @@ export default class Scroller extends Module { } cls.prototype.ffzShowFrozen = function() { + this._ffz_freeze_visible = true; let el = this._ffz_freeze_indicator; if ( ! el ) { const node = t.fine.getHostNode(this); @@ -139,6 +139,7 @@ export default class Scroller extends Module { } cls.prototype.ffzHideFrozen = function() { + this._ffz_freeze_visible = false; if ( this._ffz_freeze_indicator ) this._ffz_freeze_indicator.classList.add('hide'); } @@ -152,7 +153,7 @@ export default class Scroller extends Module { this.ffz_frozen = true; this.setState({ffzFrozen: true}); - this.ffzShowFrozen(); + //this.ffzShowFrozen(); } cls.prototype.ffzUnfreeze = function() { @@ -166,7 +167,30 @@ export default class Scroller extends Module { if ( this.state.isAutoScrolling ) this.scrollToBottom(); - this.ffzHideFrozen(); + //this.ffzHideFrozen(); + } + + + cls.prototype.ffzInstallHandler = function() { + if ( this._ffz_handleScroll ) + return; + + this._ffz_handleScroll = this.handleScrollEvent; + const t = this; + this.handleScrollEvent = function(e) { + // If we're frozen because of FFZ, do not allow a mouse click to update + // the auto-scrolling state. That just gets annoying. + if ( e.type === 'mousedown' && t.ffz_frozen ) + return; + + return t._ffz_handleScroll(e); + } + + const scroller = this.scroll && this.scroll.scrollContent; + if ( scroller ) { + scroller.removeEventListener('mousedown', this._ffz_handleScroll); + scroller.addEventListener('mousedown', this.handleScrollEvent); + } } @@ -207,6 +231,8 @@ export default class Scroller extends Module { if ( ! node ) return; + this._ffz_freeze_visible = false; + if ( this._ffz_freeze_indicator ) { node.removeChild(this._ffz_freeze_indicator); this._ffz_freeze_indicator = null; @@ -232,10 +258,10 @@ export default class Scroller extends Module { cls.prototype.ffzKey = function(e) { if (e.altKey === this.ffz_alt && - e.shiftKey === this.ffz_shift && - e.ctrlKey === this.ffz_ctrl && - e.metaKey === this.ffz_meta) - return; + e.shiftKey === this.ffz_shift && + e.ctrlKey === this.ffz_ctrl && + e.metaKey === this.ffz_meta) + return; this.ffz_alt = e.altKey; this.ffz_shift = e.shiftKey; @@ -266,12 +292,12 @@ export default class Scroller extends Module { // If nothing of interest has happened, stop. if (e.altKey === this.ffz_alt && - e.shiftKey === this.ffz_shift && - e.ctrlKey === this.ffz_ctrl && - e.metaKey === this.ffz_meta && - e.screenY === this.ffz_sy && - e.screenX === this.ffz_sx) - return; + e.shiftKey === this.ffz_shift && + e.ctrlKey === this.ffz_ctrl && + e.metaKey === this.ffz_meta && + e.screenY === this.ffz_sy && + e.screenX === this.ffz_sx) + return; this.ffz_alt = e.altKey; this.ffz_shift = e.shiftKey; @@ -291,7 +317,7 @@ export default class Scroller extends Module { } - cls.prototype.ffzMouseLeave = function(e) { + cls.prototype.ffzMouseLeave = function() { this.ffz_outside = true; if ( this._ffz_outside ) clearTimeout(this._ffz_outside); @@ -306,15 +332,29 @@ export default class Scroller extends Module { this.ChatScroller.on('mount', this.onMount, this); this.ChatScroller.on('unmount', this.onUnmount, this); + + this.ChatScroller.on('update', inst => { + const should_show = inst.ffz_freeze_enabled && inst.state.ffzFrozen && inst.state.isAutoScrolling, + changed = should_show !== inst._ffz_freeze_visible; + + if ( changed ) + if ( should_show ) + inst.ffzShowFrozen(); + else + inst.ffzHideFrozen(); + }); + } onMount(inst) { + inst.ffzInstallHandler(); + if ( this.freeze !== 0 ) inst.ffzEnableFreeze(); } - onUnmount(inst) { + onUnmount(inst) { // eslint-disable-line class-methods-use-this inst.ffzDisableFreeze(); } } \ No newline at end of file diff --git a/src/utilities/color.js b/src/utilities/color.js index 34206ab7..ba8e28a7 100644 --- a/src/utilities/color.js +++ b/src/utilities/color.js @@ -1,5 +1,4 @@ 'use strict'; -/* eslint-disable */ import {has} from 'utilities/object'; @@ -179,21 +178,21 @@ RGBAColor.fromHex = function(code, alpha = 1) { } RGBAColor.fromHSVA = function(h, s, v, a) { - let r, g, b, + let r, g, b; - i = Math.floor(h * 6), + const i = Math.floor(h * 6), f = h * 6 - i, p = v * (1 - s), q = v * (1 - f * s), t = v * (1 - (1 - f) * s); switch(i % 6) { - case 0: r = v, g = t, b = p; break; - case 1: r = q, g = v, b = p; break; - case 2: r = p, g = v, b = t; break; - case 3: r = p, g = q, b = v; break; - case 4: r = t, g = p, b = v; break; - case 5: r = v, g = p, b = q; + case 0: r = v; g = t; b = p; break; + case 1: r = q; g = v; b = p; break; + case 2: r = p; g = v; b = t; break; + case 3: r = p; g = q; b = v; break; + case 4: r = t; g = p; b = v; break; + case 5: r = v; g = p; b = q; } return new RGBAColor( @@ -237,13 +236,13 @@ RGBAColor.fromHSLA = function(h, s, l, a) { RGBAColor.prototype.toHSVA = function() { return HSVAColor.fromRGBA(this.r, this.g, this.b, this.a); } RGBAColor.prototype.toHSLA = function() { return HSLAColor.fromRGBA(this.r, this.g, this.b, this.a); } -RGBAColor.prototype.toCSS = function() { return "rgb" + (this.a !== 1 ? "a" : "") + "(" + Math.round(this.r) + "," + Math.round(this.g) + "," + Math.round(this.b) + (this.a !== 1 ? "," + this.a : "") + ")"; } +RGBAColor.prototype.toCSS = function() { return `rgb${this.a !== 1 ? 'a' : ''}(${Math.round(this.r)},${Math.round(this.g)},${Math.round(this.b)}${this.a !== 1 ? `,${this.a}` : ''})`; } RGBAColor.prototype.toXYZA = function() { return XYZAColor.fromRGBA(this.r, this.g, this.b, this.a); } RGBAColor.prototype.toLUVA = function() { return this.toXYZA().toLUVA(); } RGBAColor.prototype.toHex = function() { - var rgb = this.b | (this.g << 8) | (this.r << 16); - return '#' + (0x1000000 + rgb).toString(16).slice(1); + const rgb = this.b | (this.g << 8) | (this.r << 16); + return `#${(0x1000000 + rgb).toString(16).slice(1)}`; } @@ -253,7 +252,7 @@ RGBAColor.prototype.get_Y = function() { RGBAColor.prototype.luminance = function() { - var r = bit2linear(this.r / 255), + const r = bit2linear(this.r / 255), g = bit2linear(this.g / 255), b = bit2linear(this.b / 255); @@ -262,7 +261,7 @@ RGBAColor.prototype.luminance = function() { RGBAColor.prototype.brighten = function(amount) { - amount = typeof amount === "number" ? amount : 1; + amount = typeof amount === `number` ? amount : 1; amount = Math.round(255 * (amount / 100)); return new RGBAColor( @@ -274,43 +273,47 @@ RGBAColor.prototype.brighten = function(amount) { } -RGBAColor.prototype.daltonize = function(type, amount) { - amount = typeof amount === "number" ? amount : 1.0; - var cvd; - if ( typeof type === "string" ) { +RGBAColor.prototype.daltonize = function(type) { + let cvd; + if ( typeof type === 'string' ) { if ( Color.CVDMatrix.hasOwnProperty(type) ) cvd = Color.CVDMatrix[type]; else - throw "Invalid CVD matrix."; + throw new Error('Invalid CVD matrix'); } else cvd = type; - var cvd_a = cvd[0], cvd_b = cvd[1], cvd_c = cvd[2], + const cvd_a = cvd[0], cvd_b = cvd[1], cvd_c = cvd[2], cvd_d = cvd[3], cvd_e = cvd[4], cvd_f = cvd[5], - cvd_g = cvd[6], cvd_h = cvd[7], cvd_i = cvd[8], + cvd_g = cvd[6], cvd_h = cvd[7], cvd_i = cvd[8]; - L, M, S, l, m, s, R, G, B, RR, GG, BB; + //let L, M, S, l, m, s, R, G, B, RR, GG, BB; // RGB to LMS matrix conversion - L = (17.8824 * this.r) + (43.5161 * this.g) + (4.11935 * this.b); - M = (3.45565 * this.r) + (27.1554 * this.g) + (3.86714 * this.b); - S = (0.0299566 * this.r) + (0.184309 * this.g) + (1.46709 * this.b); + const L = (17.8824 * this.r) + (43.5161 * this.g) + (4.11935 * this.b), + M = (3.45565 * this.r) + (27.1554 * this.g) + (3.86714 * this.b), + S = (0.0299566 * this.r) + (0.184309 * this.g) + (1.46709 * this.b); + // Simulate color blindness - l = (cvd_a * L) + (cvd_b * M) + (cvd_c * S); - m = (cvd_d * L) + (cvd_e * M) + (cvd_f * S); - s = (cvd_g * L) + (cvd_h * M) + (cvd_i * S); + const l = (cvd_a * L) + (cvd_b * M) + (cvd_c * S), + m = (cvd_d * L) + (cvd_e * M) + (cvd_f * S), + s = (cvd_g * L) + (cvd_h * M) + (cvd_i * S); + // LMS to RGB matrix conversion - R = (0.0809444479 * l) + (-0.130504409 * m) + (0.116721066 * s); - G = (-0.0102485335 * l) + (0.0540193266 * m) + (-0.113614708 * s); - B = (-0.000365296938 * l) + (-0.00412161469 * m) + (0.693511405 * s); + let R = (0.0809444479 * l) + (-0.130504409 * m) + (0.116721066 * s), + G = (-0.0102485335 * l) + (0.0540193266 * m) + (-0.113614708 * s), + B = (-0.000365296938 * l) + (-0.00412161469 * m) + (0.693511405 * s); + // Isolate invisible colors to color vision deficiency (calculate error matrix) R = this.r - R; G = this.g - G; B = this.b - B; + // Shift colors towards visible spectrum (apply error modifications) - RR = (0.0 * R) + (0.0 * G) + (0.0 * B); - GG = (0.7 * R) + (1.0 * G) + (0.0 * B); - BB = (0.7 * R) + (0.0 * G) + (1.0 * B); + const RR = (0.0 * R) + (0.0 * G) + (0.0 * B), + GG = (0.7 * R) + (1.0 * G) + (0.0 * B), + BB = (0.7 * R) + (0.0 * G) + (1.0 * B); + // Add compensation to original values R = Math.min(Math.max(0, RR + this.r), 255); G = Math.min(Math.max(0, GG + this.g), 255); @@ -334,12 +337,14 @@ HSLAColor.prototype.eq = function(hsl) { HSLAColor.fromRGBA = function(r, g, b, a) { r /= 255; g /= 255; b /= 255; - var max = Math.max(r,g,b), + const max = Math.max(r,g,b), min = Math.min(r,g,b), - h, s, l = Math.min(Math.max(0, (max+min) / 2), 1), + l = Math.min(Math.max(0, (max+min) / 2), 1), d = Math.min(Math.max(0, max - min), 1); + let h, s; + if ( d === 0 ) h = s = 0; else { @@ -361,12 +366,17 @@ HSLAColor.fromRGBA = function(r, g, b, a) { } HSLAColor.prototype.targetLuminance = function (target) { - var s = this.s; + let s = this.s, + min = 0, + max = 1; + s *= Math.pow(this.l > 0.5 ? -this.l : this.l - 1, 7) + 1; - var min = 0, max = 1, d = (max - min) / 2, mid = min + d; + let d = (max - min) / 2, + mid = min + d; + for (; d > 1/65536; d /= 2, mid = min + d) { - var luminance = RGBAColor.fromHSLA(this.h, s, mid, 1).luminance() + const luminance = RGBAColor.fromHSLA(this.h, s, mid, 1).luminance() if (luminance > target) { max = mid; } else { @@ -378,7 +388,7 @@ HSLAColor.prototype.targetLuminance = function (target) { } HSLAColor.prototype.toRGBA = function() { return RGBAColor.fromHSLA(this.h, this.s, this.l, this.a); } -HSLAColor.prototype.toCSS = function() { return "hsl" + (this.a !== 1 ? "a" : "") + "(" + Math.round(this.h*360) + "," + Math.round(this.s*100) + "%," + Math.round(this.l*100) + "%" + (this.a !== 1 ? "," + this.a : "") + ")"; } +HSLAColor.prototype.toCSS = function() { return `"hsl${this.a !== 1 ? 'a' : ''}(${Math.round(this.h*360)},${Math.round(this.s*100)}%,${Math.round(this.l*100)}%${this.a !== 1 ? `,${this.a}` : ''})`; } HSLAColor.prototype.toHSVA = function() { return this.toRGBA().toHSVA(); } HSLAColor.prototype.toXYZA = function() { return this.toRGBA().toXYZA(); } HSLAColor.prototype.toLUVA = function() { return this.toRGBA().toLUVA(); } @@ -397,14 +407,15 @@ HSVAColor.prototype.eq = function(hsv) { return hsv.h === this.h && hsv.s === th HSVAColor.fromRGBA = function(r, g, b, a) { r /= 255; g /= 255; b /= 255; - var max = Math.max(r, g, b), + const max = Math.max(r, g, b), min = Math.min(r, g, b), d = Math.min(Math.max(0, max - min), 1), - h, s = max === 0 ? 0 : d / max, v = max; + let h; + if ( d === 0 ) h = 0; else { @@ -442,7 +453,7 @@ HSVAColor.prototype._a = function(a) { return new HSVAColor(this.h, this.s, this XYZAColor.prototype.eq = function(xyz) { return xyz.x === this.x && xyz.y === this.y && xyz.z === this.z; } XYZAColor.fromRGBA = function(r, g, b, a) { - var R = bit2linear(r / 255), + const R = bit2linear(r / 255), G = bit2linear(g / 255), B = bit2linear(b / 255); @@ -455,20 +466,19 @@ XYZAColor.fromRGBA = function(r, g, b, a) { } XYZAColor.fromLUVA = function(l, u, v, alpha) { - var deltaGammaFactor = 1 / (XYZAColor.WHITE.x + 15 * XYZAColor.WHITE.y + 3 * XYZAColor.WHITE.z); - var uDeltaGamma = 4 * XYZAColor.WHITE.x * deltaGammaFactor; - var vDeltagamma = 9 * XYZAColor.WHITE.y * deltaGammaFactor; + const deltaGammaFactor = 1 / (XYZAColor.WHITE.x + 15 * XYZAColor.WHITE.y + 3 * XYZAColor.WHITE.z), + uDeltaGamma = 4 * XYZAColor.WHITE.x * deltaGammaFactor, + vDeltagamma = 9 * XYZAColor.WHITE.y * deltaGammaFactor; // XYZAColor.EPSILON * XYZAColor.KAPPA = 8 - var Y = (l > 8) ? Math.pow((l + 16) / 116, 3) : l / XYZAColor.KAPPA; + const Y = (l > 8) ? Math.pow((l + 16) / 116, 3) : l / XYZAColor.KAPPA, + a = 1/3 * (((52 * l) / (u + 13 * l * uDeltaGamma)) - 1), + b = -5 * Y, + c = -1/3, + d = Y * (((39 * l) / (v + 13 * l * vDeltagamma)) - 5), - var a = 1/3 * (((52 * l) / (u + 13 * l * uDeltaGamma)) - 1); - var b = -5 * Y; - var c = -1/3; - var d = Y * (((39 * l) / (v + 13 * l * vDeltagamma)) - 5); - - var X = (d - b) / (a - c); - var Z = X * a + b; + X = (d - b) / (a - c), + Z = X * a + b; return new XYZAColor(X, Y, Z, alpha === undefined ? 1 : alpha); } @@ -496,25 +506,25 @@ XYZAColor.WHITE = (new RGBAColor(255, 255, 255, 1)).toXYZA(); LUVAColor.prototype.eq = function(luv) { return luv.l === this.l && luv.u === this.u && luv.v === this.v; } LUVAColor.fromXYZA = function(X, Y, Z, a) { - var deltaGammaFactor = 1 / (XYZAColor.WHITE.x + 15 * XYZAColor.WHITE.y + 3 * XYZAColor.WHITE.z); - var uDeltaGamma = 4 * XYZAColor.WHITE.x * deltaGammaFactor; - var vDeltagamma = 9 * XYZAColor.WHITE.y * deltaGammaFactor; + const deltaGammaFactor = 1 / (XYZAColor.WHITE.x + 15 * XYZAColor.WHITE.y + 3 * XYZAColor.WHITE.z), + uDeltaGamma = 4 * XYZAColor.WHITE.x * deltaGammaFactor, + vDeltagamma = 9 * XYZAColor.WHITE.y * deltaGammaFactor, - var yGamma = Y / XYZAColor.WHITE.y; - var deltaDivider = (X + 15 * Y + 3 * Z); + yGamma = Y / XYZAColor.WHITE.y; + let deltaDivider = (X + 15 * Y + 3 * Z); if (deltaDivider === 0) { deltaDivider = 1; } - var deltaFactor = 1 / deltaDivider; + const deltaFactor = 1 / deltaDivider, - var uDelta = 4 * X * deltaFactor; - var vDelta = 9 * Y * deltaFactor; + uDelta = 4 * X * deltaFactor, + vDelta = 9 * Y * deltaFactor, - var L = (yGamma > XYZAColor.EPSILON) ? 116 * Math.pow(yGamma, 1/3) - 16 : XYZAColor.KAPPA * yGamma; - var u = 13 * L * (uDelta - uDeltaGamma); - var v = 13 * L * (vDelta - vDeltagamma); + L = (yGamma > XYZAColor.EPSILON) ? 116 * Math.pow(yGamma, 1/3) - 16 : XYZAColor.KAPPA * yGamma, + u = 13 * L * (uDelta - uDeltaGamma), + v = 13 * L * (vDelta - vDeltagamma); return new LUVAColor(L, u, v, a === undefined ? 1 : a); } @@ -627,6 +637,17 @@ export class ColorAdjuster { const hsl = rgb.toHSLA(); rgb = hsl._l(Math.min(Math.max(0, 0.9 * hsl.l), 1)).toRGBA(); } + + } else if ( this._mode === 4 ) { + // RGB Loop + let i = 0; + if ( this._dark ) + while ( rgb.luminance() < 0.15 && i++ < 127 ) + rgb = rgb.brighten(); + + else + while ( rgb.luminance() > 0.3 && i++ < 127 ) + rgb = rgb.brighten(-1); } const out = this._cache[color] = rgb.toHex();