diff --git a/frontend_tests/node_tests/stream_list.js b/frontend_tests/node_tests/stream_list.js index d39beae562..079418f115 100644 --- a/frontend_tests/node_tests/stream_list.js +++ b/frontend_tests/node_tests/stream_list.js @@ -26,14 +26,11 @@ mock_esm("../../static/js/ui", {get_scroll_element: (element) => element}); const {Filter} = zrequire("../js/filter"); const stream_sort = zrequire("stream_sort"); -const stream_color = zrequire("stream_color"); const unread = zrequire("unread"); const stream_data = zrequire("stream_data"); const scroll_util = zrequire("scroll_util"); const stream_list = zrequire("stream_list"); -stream_color.initialize(); - const devel = { name: "devel", stream_id: 100, diff --git a/static/js/color_class.js b/static/js/color_class.js new file mode 100644 index 0000000000..20a9f8514a --- /dev/null +++ b/static/js/color_class.js @@ -0,0 +1,57 @@ +import _ from "lodash"; + +import * as colorspace from "./colorspace"; + +export function get_threshold() { + // sRGB color component for dark label text. + // 0x33 to match the color #333333 set by Bootstrap. + const label_color = 0x33; + const lightness = colorspace.luminance_to_lightness(colorspace.sRGB_to_linear(label_color)); + + // Compute midpoint lightness between that and white (100). + return (lightness + 100) / 2; +} + +const lightness_threshold = get_threshold(); + +// From a background color (in format "#fff" or "#ffffff") +// pick a CSS class (or empty string) to determine the +// text label color etc. +// +// It would be better to work with an actual data structure +// rather than a hex string, but we have to deal with values +// already saved on the server, etc. +// +// This gets called on every message, so cache the results. +export const get_css_class = _.memoize((color) => { + let match; + let i; + const channel = [0, 0, 0]; + let mult = 1; + + match = /^#([\dA-Fa-f]{2})([\dA-Fa-f]{2})([\dA-Fa-f]{2})$/.exec(color); + if (!match) { + // 3-digit shorthand; Spectrum gives this e.g. for pure black. + // Multiply each digit by 16+1. + mult = 17; + + match = /^#([\dA-Fa-f])([\dA-Fa-f])([\dA-Fa-f])$/.exec(color); + if (!match) { + // Can't understand color. + return ""; + } + } + + // CSS colors are specified in the sRGB color space. + // Convert to linear intensity values. + for (i = 0; i < 3; i += 1) { + channel[i] = colorspace.sRGB_to_linear(mult * Number.parseInt(match[i + 1], 16)); + } + + // Compute perceived lightness as CIE L*. + const lightness = colorspace.luminance_to_lightness(colorspace.rgb_luminance(channel)); + + // Determine if we're past the midpoint between the + // dark and light label lightness. + return lightness < lightness_threshold ? "dark_background" : ""; +}); diff --git a/static/js/drafts.js b/static/js/drafts.js index c470635d77..88aa696b96 100644 --- a/static/js/drafts.js +++ b/static/js/drafts.js @@ -6,6 +6,7 @@ import render_draft_table_body from "../templates/draft_table_body.hbs"; import * as blueslip from "./blueslip"; import * as browser_history from "./browser_history"; +import * as color_class from "./color_class"; import * as compose from "./compose"; import * as compose_actions from "./compose_actions"; import * as compose_fade from "./compose_fade"; @@ -16,7 +17,6 @@ import * as markdown from "./markdown"; import * as narrow from "./narrow"; import * as overlays from "./overlays"; import * as people from "./people"; -import * as stream_color from "./stream_color"; import * as stream_data from "./stream_data"; import * as timerender from "./timerender"; import * as util from "./util"; @@ -253,7 +253,7 @@ export function format_draft(draft) { is_stream: true, stream, stream_color: draft_stream_color, - dark_background: stream_color.get_color_class(draft_stream_color), + dark_background: color_class.get_css_class(draft_stream_color), topic: draft_topic, raw_content: draft.content, time_stamp, diff --git a/static/js/message_list_view.js b/static/js/message_list_view.js index 23ca47fee3..bceaa2c49e 100644 --- a/static/js/message_list_view.js +++ b/static/js/message_list_view.js @@ -9,6 +9,7 @@ import render_single_message from "../templates/single_message.hbs"; import * as activity from "./activity"; import * as blueslip from "./blueslip"; +import * as color_class from "./color_class"; import * as compose from "./compose"; import * as compose_fade from "./compose_fade"; import * as condense from "./condense"; @@ -23,7 +24,6 @@ import * as reactions from "./reactions"; import * as recent_topics from "./recent_topics"; import * as rendered_markdown from "./rendered_markdown"; import * as rows from "./rows"; -import * as stream_color from "./stream_color"; import * as stream_data from "./stream_data"; import * as submessage from "./submessage"; import * as timerender from "./timerender"; @@ -138,7 +138,7 @@ function populate_group_from_message_container(group, message_container) { if (group.is_stream) { group.background_color = stream_data.get_color(message_container.msg.stream); - group.color_class = stream_color.get_color_class(group.background_color); + group.color_class = color_class.get_css_class(group.background_color); group.invite_only = stream_data.get_invite_only(message_container.msg.stream); group.topic = message_container.msg.topic; group.match_topic = util.get_match_topic(message_container.msg); diff --git a/static/js/stream_bar.js b/static/js/stream_bar.js index 99a2e83c5e..08b7b96084 100644 --- a/static/js/stream_bar.js +++ b/static/js/stream_bar.js @@ -1,6 +1,6 @@ import $ from "jquery"; -import * as stream_color from "./stream_color"; +import * as color_class from "./color_class"; import * as stream_data from "./stream_data"; function update_lock_icon_for_stream(stream_name) { @@ -29,6 +29,6 @@ export function decorate(stream_name, element, is_compose) { } element .css("background-color", color) - .removeClass(stream_color.color_classes) - .addClass(stream_color.get_color_class(color)); + .removeClass("dark_background") + .addClass(color_class.get_css_class(color)); } diff --git a/static/js/stream_color.js b/static/js/stream_color.js index 1f1ca9fff3..790a776adb 100644 --- a/static/js/stream_color.js +++ b/static/js/stream_color.js @@ -1,74 +1,15 @@ import $ from "jquery"; -import _ from "lodash"; -import * as colorspace from "./colorspace"; +import * as color_class from "./color_class"; import * as message_view_header from "./message_view_header"; import * as subs from "./subs"; export const default_color = "#c2c2c2"; -// Classes which could be returned by get_color_class. -export const color_classes = "dark_background"; - -let lightness_threshold; - -export function initialize() { - // sRGB color component for dark label text. - // 0x33 to match the color #333333 set by Bootstrap. - const label_color = 0x33; - const lightness = colorspace.luminance_to_lightness(colorspace.sRGB_to_linear(label_color)); - - // Compute midpoint lightness between that and white (100). - lightness_threshold = (lightness + 100) / 2; -} - -// From a background color (in format "#fff" or "#ffffff") -// pick a CSS class (or empty string) to determine the -// text label color etc. -// -// It would be better to work with an actual data structure -// rather than a hex string, but we have to deal with values -// already saved on the server, etc. -// -// This gets called on every message, so cache the results. -export const get_color_class = _.memoize((color) => { - let match; - let i; - const channel = [0, 0, 0]; - let mult = 1; - - match = /^#([\dA-Fa-f]{2})([\dA-Fa-f]{2})([\dA-Fa-f]{2})$/.exec(color); - if (!match) { - // 3-digit shorthand; Spectrum gives this e.g. for pure black. - // Multiply each digit by 16+1. - mult = 17; - - match = /^#([\dA-Fa-f])([\dA-Fa-f])([\dA-Fa-f])$/.exec(color); - if (!match) { - // Can't understand color. - return ""; - } - } - - // CSS colors are specified in the sRGB color space. - // Convert to linear intensity values. - for (i = 0; i < 3; i += 1) { - channel[i] = colorspace.sRGB_to_linear(mult * Number.parseInt(match[i + 1], 16)); - } - - // Compute perceived lightness as CIE L*. - const lightness = colorspace.luminance_to_lightness(colorspace.rgb_luminance(channel)); - - // Determine if we're past the midpoint between the - // dark and light label lightness. - return lightness < lightness_threshold ? "dark_background" : ""; -}); - function update_table_stream_color(table, stream_name, color) { // This is ugly, but temporary, as the new design will make it // so that we only have color in the headers. const style = color; - const color_class = get_color_class(color); const stream_labels = $("#floating_recipient_bar").add(table).find(".stream_label"); @@ -89,8 +30,8 @@ function update_table_stream_color(table, stream_name, color) { "inset 2px 0px 0px 0px " + style + ", -1px 0px 0px 0px " + style, ); $label.css({background: style, "border-left-color": style}); - $label.removeClass(color_classes); - $label.addClass(color_class); + $label.removeClass("dark_background"); + $label.addClass(color_class.get_css_class(color)); } } } diff --git a/static/js/stream_list.js b/static/js/stream_list.js index 07724f11d0..b420c1401f 100644 --- a/static/js/stream_list.js +++ b/static/js/stream_list.js @@ -5,6 +5,7 @@ import render_stream_privacy from "../templates/stream_privacy.hbs"; import render_stream_sidebar_row from "../templates/stream_sidebar_row.hbs"; import * as blueslip from "./blueslip"; +import * as color_class from "./color_class"; import * as hash_util from "./hash_util"; import * as keydown_util from "./keydown_util"; import {ListCursor} from "./list_cursor"; @@ -13,7 +14,6 @@ import * as narrow_state from "./narrow_state"; import * as popovers from "./popovers"; import * as resize from "./resize"; import * as scroll_util from "./scroll_util"; -import * as stream_color from "./stream_color"; import * as stream_data from "./stream_data"; import * as stream_popover from "./stream_popover"; import * as stream_sort from "./stream_sort"; @@ -254,7 +254,7 @@ function build_stream_sidebar_li(sub) { color: sub.color, pin_to_top: sub.pin_to_top, }; - args.dark_background = stream_color.get_color_class(args.color); + args.dark_background = color_class.get_css_class(args.color); const list_item = $(render_stream_sidebar_row(args)); return list_item; } @@ -311,7 +311,7 @@ export function redraw_stream_privacy(sub) { } const div = li.find(".stream-privacy"); - const dark_background = stream_color.get_color_class(sub.color); + const dark_background = color_class.get_css_class(sub.color); const args = { invite_only: sub.invite_only, diff --git a/static/js/ui_init.js b/static/js/ui_init.js index ba654322b9..94bc73bea8 100644 --- a/static/js/ui_init.js +++ b/static/js/ui_init.js @@ -56,7 +56,6 @@ import * as settings_toggle from "./settings_toggle"; import * as spoilers from "./spoilers"; import * as starred_messages from "./starred_messages"; import * as stream_bar from "./stream_bar"; -import * as stream_color from "./stream_color"; import * as stream_data from "./stream_data"; import * as stream_edit from "./stream_edit"; import * as stream_list from "./stream_list"; @@ -481,7 +480,6 @@ export function initialize_everything() { message_viewport.initialize(); initialize_kitchen_sink_stuff(); echo.initialize(); - stream_color.initialize(); stream_edit.initialize(); stream_data.initialize(stream_data_params); pm_conversations.recent.initialize(pm_conversations_params);