zulip/static/js/resize.js

280 lines
9.2 KiB
JavaScript
Raw Normal View History

const util = require("./util");
const autosize = require('autosize');
let narrow_window = false;
function confine_to_range(lo, val, hi) {
if (val < lo) {
return lo;
}
if (val > hi) {
return hi;
}
return val;
}
function size_blocks(blocks, usable_height) {
let sum_height = 0;
js: Automatically convert _.each to for…of. This commit was automatically generated by the following script, followed by lint --fix and a few small manual lint-related cleanups. import * as babelParser from "recast/parsers/babel"; import * as recast from "recast"; import * as tsParser from "recast/parsers/typescript"; import { builders as b, namedTypes as n } from "ast-types"; import { Context } from "ast-types/lib/path-visitor"; import K from "ast-types/gen/kinds"; import { NodePath } from "ast-types/lib/node-path"; import assert from "assert"; import fs from "fs"; import path from "path"; import process from "process"; const checkExpression = (node: n.Node): node is K.ExpressionKind => n.Expression.check(node); const checkStatement = (node: n.Node): node is K.StatementKind => n.Statement.check(node); for (const file of process.argv.slice(2)) { console.log("Parsing", file); const ast = recast.parse(fs.readFileSync(file, { encoding: "utf8" }), { parser: path.extname(file) === ".ts" ? tsParser : babelParser, }); let changed = false; let inLoop = false; let replaceReturn = false; const visitLoop = (...args: string[]) => function(this: Context, path: NodePath) { for (const arg of args) { this.visit(path.get(arg)); } const old = { inLoop }; inLoop = true; this.visit(path.get("body")); inLoop = old.inLoop; return false; }; recast.visit(ast, { visitDoWhileStatement: visitLoop("test"), visitExpressionStatement(path) { const { expression, comments } = path.node; let valueOnly; if ( n.CallExpression.check(expression) && n.MemberExpression.check(expression.callee) && !expression.callee.computed && n.Identifier.check(expression.callee.object) && expression.callee.object.name === "_" && n.Identifier.check(expression.callee.property) && ["each", "forEach"].includes(expression.callee.property.name) && [2, 3].includes(expression.arguments.length) && checkExpression(expression.arguments[0]) && (n.FunctionExpression.check(expression.arguments[1]) || n.ArrowFunctionExpression.check(expression.arguments[1])) && [1, 2].includes(expression.arguments[1].params.length) && n.Identifier.check(expression.arguments[1].params[0]) && ((valueOnly = expression.arguments[1].params[1] === undefined) || n.Identifier.check(expression.arguments[1].params[1])) && (expression.arguments[2] === undefined || n.ThisExpression.check(expression.arguments[2])) ) { const old = { inLoop, replaceReturn }; inLoop = false; replaceReturn = true; this.visit( path .get("expression") .get("arguments") .get(1) .get("body") ); inLoop = old.inLoop; replaceReturn = old.replaceReturn; const [right, { body, params }] = expression.arguments; const loop = b.forOfStatement( b.variableDeclaration("let", [ b.variableDeclarator( valueOnly ? params[0] : b.arrayPattern([params[1], params[0]]) ), ]), valueOnly ? right : b.callExpression( b.memberExpression(right, b.identifier("entries")), [] ), checkStatement(body) ? body : b.expressionStatement(body) ); loop.comments = comments; path.replace(loop); changed = true; } this.traverse(path); }, visitForStatement: visitLoop("init", "test", "update"), visitForInStatement: visitLoop("left", "right"), visitForOfStatement: visitLoop("left", "right"), visitFunction(path) { this.visit(path.get("params")); const old = { replaceReturn }; replaceReturn = false; this.visit(path.get("body")); replaceReturn = old.replaceReturn; return false; }, visitReturnStatement(path) { if (replaceReturn) { assert(!inLoop); // could use labeled continue if this ever fires const { argument, comments } = path.node; if (argument === null) { const s = b.continueStatement(); s.comments = comments; path.replace(s); } else { const s = b.expressionStatement(argument); s.comments = comments; path.replace(s, b.continueStatement()); } return false; } this.traverse(path); }, visitWhileStatement: visitLoop("test"), }); if (changed) { console.log("Writing", file); fs.writeFileSync(file, recast.print(ast).code, { encoding: "utf8" }); } } Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-06 06:19:47 +01:00
for (const block of blocks) {
sum_height += block.real_height;
js: Automatically convert _.each to for…of. This commit was automatically generated by the following script, followed by lint --fix and a few small manual lint-related cleanups. import * as babelParser from "recast/parsers/babel"; import * as recast from "recast"; import * as tsParser from "recast/parsers/typescript"; import { builders as b, namedTypes as n } from "ast-types"; import { Context } from "ast-types/lib/path-visitor"; import K from "ast-types/gen/kinds"; import { NodePath } from "ast-types/lib/node-path"; import assert from "assert"; import fs from "fs"; import path from "path"; import process from "process"; const checkExpression = (node: n.Node): node is K.ExpressionKind => n.Expression.check(node); const checkStatement = (node: n.Node): node is K.StatementKind => n.Statement.check(node); for (const file of process.argv.slice(2)) { console.log("Parsing", file); const ast = recast.parse(fs.readFileSync(file, { encoding: "utf8" }), { parser: path.extname(file) === ".ts" ? tsParser : babelParser, }); let changed = false; let inLoop = false; let replaceReturn = false; const visitLoop = (...args: string[]) => function(this: Context, path: NodePath) { for (const arg of args) { this.visit(path.get(arg)); } const old = { inLoop }; inLoop = true; this.visit(path.get("body")); inLoop = old.inLoop; return false; }; recast.visit(ast, { visitDoWhileStatement: visitLoop("test"), visitExpressionStatement(path) { const { expression, comments } = path.node; let valueOnly; if ( n.CallExpression.check(expression) && n.MemberExpression.check(expression.callee) && !expression.callee.computed && n.Identifier.check(expression.callee.object) && expression.callee.object.name === "_" && n.Identifier.check(expression.callee.property) && ["each", "forEach"].includes(expression.callee.property.name) && [2, 3].includes(expression.arguments.length) && checkExpression(expression.arguments[0]) && (n.FunctionExpression.check(expression.arguments[1]) || n.ArrowFunctionExpression.check(expression.arguments[1])) && [1, 2].includes(expression.arguments[1].params.length) && n.Identifier.check(expression.arguments[1].params[0]) && ((valueOnly = expression.arguments[1].params[1] === undefined) || n.Identifier.check(expression.arguments[1].params[1])) && (expression.arguments[2] === undefined || n.ThisExpression.check(expression.arguments[2])) ) { const old = { inLoop, replaceReturn }; inLoop = false; replaceReturn = true; this.visit( path .get("expression") .get("arguments") .get(1) .get("body") ); inLoop = old.inLoop; replaceReturn = old.replaceReturn; const [right, { body, params }] = expression.arguments; const loop = b.forOfStatement( b.variableDeclaration("let", [ b.variableDeclarator( valueOnly ? params[0] : b.arrayPattern([params[1], params[0]]) ), ]), valueOnly ? right : b.callExpression( b.memberExpression(right, b.identifier("entries")), [] ), checkStatement(body) ? body : b.expressionStatement(body) ); loop.comments = comments; path.replace(loop); changed = true; } this.traverse(path); }, visitForStatement: visitLoop("init", "test", "update"), visitForInStatement: visitLoop("left", "right"), visitForOfStatement: visitLoop("left", "right"), visitFunction(path) { this.visit(path.get("params")); const old = { replaceReturn }; replaceReturn = false; this.visit(path.get("body")); replaceReturn = old.replaceReturn; return false; }, visitReturnStatement(path) { if (replaceReturn) { assert(!inLoop); // could use labeled continue if this ever fires const { argument, comments } = path.node; if (argument === null) { const s = b.continueStatement(); s.comments = comments; path.replace(s); } else { const s = b.expressionStatement(argument); s.comments = comments; path.replace(s, b.continueStatement()); } return false; } this.traverse(path); }, visitWhileStatement: visitLoop("test"), }); if (changed) { console.log("Writing", file); fs.writeFileSync(file, recast.print(ast).code, { encoding: "utf8" }); } } Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-06 06:19:47 +01:00
}
js: Automatically convert _.each to for…of. This commit was automatically generated by the following script, followed by lint --fix and a few small manual lint-related cleanups. import * as babelParser from "recast/parsers/babel"; import * as recast from "recast"; import * as tsParser from "recast/parsers/typescript"; import { builders as b, namedTypes as n } from "ast-types"; import { Context } from "ast-types/lib/path-visitor"; import K from "ast-types/gen/kinds"; import { NodePath } from "ast-types/lib/node-path"; import assert from "assert"; import fs from "fs"; import path from "path"; import process from "process"; const checkExpression = (node: n.Node): node is K.ExpressionKind => n.Expression.check(node); const checkStatement = (node: n.Node): node is K.StatementKind => n.Statement.check(node); for (const file of process.argv.slice(2)) { console.log("Parsing", file); const ast = recast.parse(fs.readFileSync(file, { encoding: "utf8" }), { parser: path.extname(file) === ".ts" ? tsParser : babelParser, }); let changed = false; let inLoop = false; let replaceReturn = false; const visitLoop = (...args: string[]) => function(this: Context, path: NodePath) { for (const arg of args) { this.visit(path.get(arg)); } const old = { inLoop }; inLoop = true; this.visit(path.get("body")); inLoop = old.inLoop; return false; }; recast.visit(ast, { visitDoWhileStatement: visitLoop("test"), visitExpressionStatement(path) { const { expression, comments } = path.node; let valueOnly; if ( n.CallExpression.check(expression) && n.MemberExpression.check(expression.callee) && !expression.callee.computed && n.Identifier.check(expression.callee.object) && expression.callee.object.name === "_" && n.Identifier.check(expression.callee.property) && ["each", "forEach"].includes(expression.callee.property.name) && [2, 3].includes(expression.arguments.length) && checkExpression(expression.arguments[0]) && (n.FunctionExpression.check(expression.arguments[1]) || n.ArrowFunctionExpression.check(expression.arguments[1])) && [1, 2].includes(expression.arguments[1].params.length) && n.Identifier.check(expression.arguments[1].params[0]) && ((valueOnly = expression.arguments[1].params[1] === undefined) || n.Identifier.check(expression.arguments[1].params[1])) && (expression.arguments[2] === undefined || n.ThisExpression.check(expression.arguments[2])) ) { const old = { inLoop, replaceReturn }; inLoop = false; replaceReturn = true; this.visit( path .get("expression") .get("arguments") .get(1) .get("body") ); inLoop = old.inLoop; replaceReturn = old.replaceReturn; const [right, { body, params }] = expression.arguments; const loop = b.forOfStatement( b.variableDeclaration("let", [ b.variableDeclarator( valueOnly ? params[0] : b.arrayPattern([params[1], params[0]]) ), ]), valueOnly ? right : b.callExpression( b.memberExpression(right, b.identifier("entries")), [] ), checkStatement(body) ? body : b.expressionStatement(body) ); loop.comments = comments; path.replace(loop); changed = true; } this.traverse(path); }, visitForStatement: visitLoop("init", "test", "update"), visitForInStatement: visitLoop("left", "right"), visitForOfStatement: visitLoop("left", "right"), visitFunction(path) { this.visit(path.get("params")); const old = { replaceReturn }; replaceReturn = false; this.visit(path.get("body")); replaceReturn = old.replaceReturn; return false; }, visitReturnStatement(path) { if (replaceReturn) { assert(!inLoop); // could use labeled continue if this ever fires const { argument, comments } = path.node; if (argument === null) { const s = b.continueStatement(); s.comments = comments; path.replace(s); } else { const s = b.expressionStatement(argument); s.comments = comments; path.replace(s, b.continueStatement()); } return false; } this.traverse(path); }, visitWhileStatement: visitLoop("test"), }); if (changed) { console.log("Writing", file); fs.writeFileSync(file, recast.print(ast).code, { encoding: "utf8" }); } } Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-06 06:19:47 +01:00
for (const block of blocks) {
let ratio = block.real_height / sum_height;
ratio = confine_to_range(0.05, ratio, 0.85);
block.max_height = confine_to_range(80, usable_height * ratio, 1.2 * block.real_height);
js: Automatically convert _.each to for…of. This commit was automatically generated by the following script, followed by lint --fix and a few small manual lint-related cleanups. import * as babelParser from "recast/parsers/babel"; import * as recast from "recast"; import * as tsParser from "recast/parsers/typescript"; import { builders as b, namedTypes as n } from "ast-types"; import { Context } from "ast-types/lib/path-visitor"; import K from "ast-types/gen/kinds"; import { NodePath } from "ast-types/lib/node-path"; import assert from "assert"; import fs from "fs"; import path from "path"; import process from "process"; const checkExpression = (node: n.Node): node is K.ExpressionKind => n.Expression.check(node); const checkStatement = (node: n.Node): node is K.StatementKind => n.Statement.check(node); for (const file of process.argv.slice(2)) { console.log("Parsing", file); const ast = recast.parse(fs.readFileSync(file, { encoding: "utf8" }), { parser: path.extname(file) === ".ts" ? tsParser : babelParser, }); let changed = false; let inLoop = false; let replaceReturn = false; const visitLoop = (...args: string[]) => function(this: Context, path: NodePath) { for (const arg of args) { this.visit(path.get(arg)); } const old = { inLoop }; inLoop = true; this.visit(path.get("body")); inLoop = old.inLoop; return false; }; recast.visit(ast, { visitDoWhileStatement: visitLoop("test"), visitExpressionStatement(path) { const { expression, comments } = path.node; let valueOnly; if ( n.CallExpression.check(expression) && n.MemberExpression.check(expression.callee) && !expression.callee.computed && n.Identifier.check(expression.callee.object) && expression.callee.object.name === "_" && n.Identifier.check(expression.callee.property) && ["each", "forEach"].includes(expression.callee.property.name) && [2, 3].includes(expression.arguments.length) && checkExpression(expression.arguments[0]) && (n.FunctionExpression.check(expression.arguments[1]) || n.ArrowFunctionExpression.check(expression.arguments[1])) && [1, 2].includes(expression.arguments[1].params.length) && n.Identifier.check(expression.arguments[1].params[0]) && ((valueOnly = expression.arguments[1].params[1] === undefined) || n.Identifier.check(expression.arguments[1].params[1])) && (expression.arguments[2] === undefined || n.ThisExpression.check(expression.arguments[2])) ) { const old = { inLoop, replaceReturn }; inLoop = false; replaceReturn = true; this.visit( path .get("expression") .get("arguments") .get(1) .get("body") ); inLoop = old.inLoop; replaceReturn = old.replaceReturn; const [right, { body, params }] = expression.arguments; const loop = b.forOfStatement( b.variableDeclaration("let", [ b.variableDeclarator( valueOnly ? params[0] : b.arrayPattern([params[1], params[0]]) ), ]), valueOnly ? right : b.callExpression( b.memberExpression(right, b.identifier("entries")), [] ), checkStatement(body) ? body : b.expressionStatement(body) ); loop.comments = comments; path.replace(loop); changed = true; } this.traverse(path); }, visitForStatement: visitLoop("init", "test", "update"), visitForInStatement: visitLoop("left", "right"), visitForOfStatement: visitLoop("left", "right"), visitFunction(path) { this.visit(path.get("params")); const old = { replaceReturn }; replaceReturn = false; this.visit(path.get("body")); replaceReturn = old.replaceReturn; return false; }, visitReturnStatement(path) { if (replaceReturn) { assert(!inLoop); // could use labeled continue if this ever fires const { argument, comments } = path.node; if (argument === null) { const s = b.continueStatement(); s.comments = comments; path.replace(s); } else { const s = b.expressionStatement(argument); s.comments = comments; path.replace(s, b.continueStatement()); } return false; } this.traverse(path); }, visitWhileStatement: visitLoop("test"), }); if (changed) { console.log("Writing", file); fs.writeFileSync(file, recast.print(ast).code, { encoding: "utf8" }); } } Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-06 06:19:47 +01:00
}
}
function set_user_list_heights(res, usable_height, buddy_list_wrapper, group_pms) {
// Calculate these heights:
// res.buddy_list_wrapper_max_height
// res.group_pms_max_height
const blocks = [
{
real_height: ui.get_scroll_element(buddy_list_wrapper).prop('scrollHeight'),
},
{
real_height: ui.get_scroll_element(group_pms).prop('scrollHeight'),
},
];
size_blocks(blocks, usable_height);
res.buddy_list_wrapper_max_height = blocks[0].max_height;
res.group_pms_max_height = blocks[1].max_height;
}
function get_new_heights() {
const res = {};
const viewport_height = message_viewport.height();
const top_navbar_height = $("#top_navbar").safeOuterHeight(true);
const invite_user_link_height = $("#invite-user-link").safeOuterHeight(true) || 0;
res.bottom_whitespace_height = viewport_height * 0.4;
res.main_div_min_height = viewport_height - top_navbar_height;
res.stream_filters_max_height = viewport_height
- parseInt($("#left-sidebar").css("marginTop"), 10)
- parseInt($(".narrows_panel").css("marginTop"), 10)
- parseInt($(".narrows_panel").css("marginBottom"), 10)
- $("#global_filters").safeOuterHeight(true)
- $("#streams_header").safeOuterHeight(true);
// Don't let us crush the stream sidebar completely out of view
res.stream_filters_max_height = Math.max(80, res.stream_filters_max_height);
// RIGHT SIDEBAR
const buddy_list_wrapper = $('#buddy_list_wrapper').expectOne();
const group_pms = $('#group-pms').expectOne();
const usable_height = viewport_height
- parseInt($("#right-sidebar").css("marginTop"), 10)
- $("#userlist-header").safeOuterHeight(true)
- $("#user_search_section").safeOuterHeight(true)
- invite_user_link_height
- parseInt(group_pms.css("marginTop"), 10)
- parseInt(group_pms.css("marginBottom"), 10)
- $("#group-pm-header").safeOuterHeight(true)
- $("#sidebar-keyboard-shortcuts").safeOuterHeight(true);
// set these
// res.buddy_list_wrapper_max_height
// res.group_pms_max_height
set_user_list_heights(
res,
usable_height,
buddy_list_wrapper,
group_pms
);
return res;
}
function left_userlist_get_new_heights() {
const res = {};
const viewport_height = message_viewport.height();
const viewport_width = message_viewport.width();
res.viewport_height = viewport_height;
res.viewport_width = viewport_width;
// main div
const top_navbar_height = $(".header").safeOuterHeight(true);
res.bottom_whitespace_height = viewport_height * 0.4;
res.main_div_min_height = viewport_height - top_navbar_height;
// left sidebar
const stream_filters = $('#stream_filters').expectOne();
const buddy_list_wrapper = $('#buddy_list_wrapper').expectOne();
const stream_filters_real_height = stream_filters.prop("scrollHeight");
const user_list_real_height = ui.get_scroll_element(buddy_list_wrapper).prop("scrollHeight");
res.total_leftlist_height = viewport_height
- parseInt($("#left-sidebar").css("marginTop"), 10)
- parseInt($(".narrows_panel").css("marginTop"), 10)
- parseInt($(".narrows_panel").css("marginBottom"), 10)
- $("#global_filters").safeOuterHeight(true)
- $("#streams_header").safeOuterHeight(true)
- $("#userlist-header").safeOuterHeight(true)
- $("#user_search_section").safeOuterHeight(true)
- parseInt(stream_filters.css("marginBottom"), 10);
const blocks = [
{
real_height: stream_filters_real_height,
},
{
real_height: user_list_real_height,
},
];
size_blocks(blocks, res.total_leftlist_height);
res.stream_filters_max_height = blocks[0].max_height;
res.buddy_list_wrapper_max_height = blocks[1].max_height;
res.group_pms_max_height = 0;
return res;
}
exports.watch_manual_resize = function (element) {
return (function on_box_resize(cb) {
const box = document.querySelector(element);
if (!box) {
blueslip.error('Bad selector in watch_manual_resize: ' + element);
return;
}
const meta = {
box: box,
height: null,
mousedown: false,
};
const box_handler = function () {
meta.mousedown = true;
meta.height = meta.box.clientHeight;
};
meta.box.addEventListener("mousedown", box_handler);
// If the user resizes the textarea manually, we use the
// callback to stop autosize from adjusting the height.
const body_handler = function () {
if (meta.mousedown === true) {
meta.mousedown = false;
if (meta.height !== meta.box.clientHeight) {
meta.height = meta.box.clientHeight;
cb.call(meta.box, meta.height);
}
}
};
document.body.addEventListener("mouseup", body_handler);
return [box_handler, body_handler];
}(function (height) {
// This callback disables autosize on the textarea. It
// will be re-enabled when this component is next opened.
autosize.destroy($(element))
.height(height + "px");
}));
};
exports.resize_bottom_whitespace = function (h) {
$("#bottom_whitespace").height(h.bottom_whitespace_height);
};
exports.resize_stream_filters_container = function (h) {
h = narrow_window ? left_userlist_get_new_heights() : get_new_heights();
exports.resize_bottom_whitespace(h);
$("#stream-filters-container").css('max-height', h.stream_filters_max_height);
};
exports.resize_sidebars = function () {
let sidebar;
if (page_params.left_side_userlist) {
const css_narrow_mode = message_viewport.is_narrow();
$("#top_navbar").removeClass("rightside-userlist");
const right_items = $('.right-sidebar-items').expectOne();
if (css_narrow_mode && !narrow_window) {
// move stuff to the left sidebar (skinny mode)
narrow_window = true;
popovers.set_userlist_placement("left");
sidebar = $("#left-sidebar").expectOne();
sidebar.append(right_items);
$("#buddy_list_wrapper").css("margin", "0px");
$("#userlist-toggle").css("display", "none");
$("#invite-user-link").hide();
} else if (!css_narrow_mode && narrow_window) {
// move stuff to the right sidebar (wide mode)
narrow_window = false;
popovers.set_userlist_placement("right");
sidebar = $("#right-sidebar").expectOne();
sidebar.append(right_items);
$("#buddy_list_wrapper").css("margin", '');
$("#userlist-toggle").css("display", '');
$("#invite-user-link").show();
}
}
const h = narrow_window ? left_userlist_get_new_heights() : get_new_heights();
$("#buddy_list_wrapper").css('max-height', h.buddy_list_wrapper_max_height);
$("#group-pms").css('max-height', h.group_pms_max_height);
$("#stream-filters-container").css('max-height', h.stream_filters_max_height);
return h;
};
exports.resize_page_components = function () {
const h = exports.resize_sidebars();
exports.resize_bottom_whitespace(h);
panels.resize_app();
};
let _old_width = $(window).width();
exports.handler = function () {
const new_width = $(window).width();
if (new_width !== _old_width) {
_old_width = new_width;
condense.clear_message_content_height_cache();
}
// On mobile web, we want to avoid hiding a popover here,
// especially if this resize was triggered by a virtual keyboard
// popping up when the user opened that very popover.
const mobile = util.is_mobile();
if (!mobile) {
popovers.hide_all();
}
exports.resize_page_components();
// Re-compute and display/remove [More] links to messages
condense.condense_and_collapse($(".message_table .message_row"));
// This function might run onReady (if we're in a narrow window),
// but before we've loaded in the messages; in that case, don't
// try to scroll to one.
if (current_msg_list.selected_id() !== -1) {
if (mobile) {
popovers.set_suppress_scroll_hide();
}
navigate.scroll_to_selected();
}
};
window.resize = exports;