mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-04 05:53:43 +00: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>
			
			
This commit is contained in:
		
				
					committed by
					
						
						Tim Abbott
					
				
			
			
				
	
			
			
			
						parent
						
							811e128787
						
					
				
				
					commit
					02511bff1c
				
			@@ -53,14 +53,14 @@ const bot_with_owner = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function make_people() {
 | 
			
		||||
    _.each(_.range(1002, 2000), (i) => {
 | 
			
		||||
    for (const i of _.range(1002, 2000)) {
 | 
			
		||||
        const person = {
 | 
			
		||||
            user_id: i,
 | 
			
		||||
            full_name: `Human ${i}`,
 | 
			
		||||
            email: `person${i}@example.com`,
 | 
			
		||||
        };
 | 
			
		||||
        people.add_in_realm(person);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    people.add_in_realm(bot);
 | 
			
		||||
    people.add_in_realm(bot_with_owner);
 | 
			
		||||
@@ -85,15 +85,14 @@ function activate_people() {
 | 
			
		||||
    presence.set_info_for_user(selma.user_id, info, server_time);
 | 
			
		||||
    presence.set_info_for_user(me.user_id, info, server_time);
 | 
			
		||||
 | 
			
		||||
    _.each(_.range(1000, 1400), (user_id) => {
 | 
			
		||||
    for (const user_id of _.range(1000, 1400)) {
 | 
			
		||||
        presence.set_info_for_user(user_id, info, server_time);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // And then 300 not active
 | 
			
		||||
    _.each(_.range(1400, 1700), (user_id) => {
 | 
			
		||||
    for (const user_id of _.range(1400, 1700)) {
 | 
			
		||||
        presence.set_info_for_user(user_id, {}, server_time);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -35,9 +35,9 @@ run_test('pick_color', () => {
 | 
			
		||||
        'yellow',
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    _.each(expected_colors, (expected_color) => {
 | 
			
		||||
    for (const expected_color of expected_colors) {
 | 
			
		||||
        assert.equal(color_data.pick_color(), expected_color);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    color_data.claim_color('blue');
 | 
			
		||||
    assert.equal(color_data.pick_color(), 'orange');
 | 
			
		||||
 
 | 
			
		||||
@@ -67,9 +67,9 @@ run_test('basics', () => {
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        self.removeClass = function (c) {
 | 
			
		||||
            _.each(tabs, function (tab) {
 | 
			
		||||
            for (const tab of tabs) {
 | 
			
		||||
                tab.removeClass(c);
 | 
			
		||||
            });
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        self.eq = function (idx) {
 | 
			
		||||
 
 | 
			
		||||
@@ -929,8 +929,7 @@ run_test('warn_if_private_stream_is_linked', () => {
 | 
			
		||||
    compose.warn_if_private_stream_is_linked(denmark);
 | 
			
		||||
    assert.equal($('#compose_private_stream_alert').visible(), true);
 | 
			
		||||
 | 
			
		||||
    _.each(checks, function (f) { f(); });
 | 
			
		||||
 | 
			
		||||
    for (const f of checks) { f(); }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -1175,7 +1174,7 @@ run_test('warn_if_mentioning_unsubscribed_user', () => {
 | 
			
		||||
    compose.warn_if_mentioning_unsubscribed_user(mentioned);
 | 
			
		||||
    assert.equal($('#compose_invite_users').visible(), true);
 | 
			
		||||
 | 
			
		||||
    _.each(checks, function (f) { f(); });
 | 
			
		||||
    for (const f of checks) { f(); }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // Simulate that the row was added to the DOM.
 | 
			
		||||
 
 | 
			
		||||
@@ -116,7 +116,7 @@ run_test('translate_emoticons_to_names', () => {
 | 
			
		||||
        {name: 'before end of sentence', original: 'Hello <original>!', expected: 'Hello <converted>!'},
 | 
			
		||||
    ];
 | 
			
		||||
    for (const [shortcut, full_name] of Object.entries(emoji_codes.emoticon_conversions)) {
 | 
			
		||||
        _.each(testcases, (t) => {
 | 
			
		||||
        for (const t of testcases) {
 | 
			
		||||
            const converted_value = full_name;
 | 
			
		||||
            let original = t.original;
 | 
			
		||||
            let expected = t.expected;
 | 
			
		||||
@@ -125,6 +125,6 @@ run_test('translate_emoticons_to_names', () => {
 | 
			
		||||
                .replace(/(<converted>)/g, converted_value);
 | 
			
		||||
            const result = emoji.translate_emoticons_to_names(original);
 | 
			
		||||
            assert.equal(result, expected);
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -14,9 +14,9 @@ run_test('initialize', () => {
 | 
			
		||||
        assert.equal(ele.icon, icon);
 | 
			
		||||
        assert.equal(ele.emojis.length, num);
 | 
			
		||||
        function check_emojis(val) {
 | 
			
		||||
            _.each(ele.emojis, function (emoji) {
 | 
			
		||||
            for (const emoji of ele.emojis) {
 | 
			
		||||
                assert.equal(emoji.is_realm_emoji, val);
 | 
			
		||||
            });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (ele.name === 'Custom') {
 | 
			
		||||
            check_emojis(true);
 | 
			
		||||
 
 | 
			
		||||
@@ -170,9 +170,9 @@ run_test('basic_chars', () => {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function assert_unmapped(s) {
 | 
			
		||||
        _.each(s, function (c) {
 | 
			
		||||
        for (const c of s) {
 | 
			
		||||
            assert.equal(process(c), false);
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Unmapped keys should immediately return false, without
 | 
			
		||||
@@ -215,18 +215,18 @@ run_test('basic_chars', () => {
 | 
			
		||||
        assert_unmapped('~!@#$%^*()_+{}:"<>');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _.each([return_true, return_false], function (settings_open) {
 | 
			
		||||
        _.each([return_true, return_false], function (is_active) {
 | 
			
		||||
            _.each([return_true, return_false], function (info_overlay_open) {
 | 
			
		||||
    for (const settings_open of [return_true, return_false]) {
 | 
			
		||||
        for (const is_active of [return_true, return_false]) {
 | 
			
		||||
            for (const info_overlay_open of [return_true, return_false]) {
 | 
			
		||||
                set_global('overlays', {
 | 
			
		||||
                    is_active: is_active,
 | 
			
		||||
                    settings_open: settings_open,
 | 
			
		||||
                    info_overlay_open: info_overlay_open,
 | 
			
		||||
                });
 | 
			
		||||
                test_normal_typing();
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Ok, now test keys that work when we're viewing messages.
 | 
			
		||||
    hotkey.processing_text = return_false;
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@ people.add(alice);
 | 
			
		||||
function test_helper(side_effects) {
 | 
			
		||||
    const events = [];
 | 
			
		||||
 | 
			
		||||
    _.each(side_effects, (side_effect) => {
 | 
			
		||||
    for (const side_effect of side_effects) {
 | 
			
		||||
        const parts = side_effect.split('.');
 | 
			
		||||
        const module = parts[0];
 | 
			
		||||
        const field = parts[1];
 | 
			
		||||
@@ -35,7 +35,7 @@ function test_helper(side_effects) {
 | 
			
		||||
        global[module][field] = () => {
 | 
			
		||||
            events.push(side_effect);
 | 
			
		||||
        };
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const self = {};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -93,9 +93,9 @@ run_test('basics', () => {
 | 
			
		||||
    });
 | 
			
		||||
    assert_contents(mld, [120, 130, 140, 145]);
 | 
			
		||||
 | 
			
		||||
    _.each(mld.all_messages(), (msg) => {
 | 
			
		||||
    for (const msg of mld.all_messages()) {
 | 
			
		||||
        msg.unread = false;
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    assert.equal(mld.first_unread_message_id(), 145);
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -110,10 +110,10 @@ run_test('msg_edited_vars', () => {
 | 
			
		||||
        const message_group = build_message_group(messages);
 | 
			
		||||
        const list = build_list([message_group]);
 | 
			
		||||
 | 
			
		||||
        _.each(messages, function (message_container) {
 | 
			
		||||
        for (const message_container of messages) {
 | 
			
		||||
            list._maybe_format_me_message(message_container);
 | 
			
		||||
            list._add_msg_edited_vars(message_container);
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const result = list._message_groups[0].message_containers;
 | 
			
		||||
 | 
			
		||||
@@ -564,11 +564,11 @@ run_test('render_windows', () => {
 | 
			
		||||
        // the list where we can move the pointer without forcing
 | 
			
		||||
        // a re-render.  The code avoids hasty re-renders for
 | 
			
		||||
        // performance reasons.
 | 
			
		||||
        _.each(_.range(start, end), function (idx) {
 | 
			
		||||
        for (const idx of _.range(start, end)) {
 | 
			
		||||
            list.selected_idx = function () { return idx; };
 | 
			
		||||
            const rendered = view.maybe_rerender();
 | 
			
		||||
            assert.equal(rendered, false);
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function verify_move(idx, range) {
 | 
			
		||||
 
 | 
			
		||||
@@ -20,11 +20,11 @@ function test_with(fixture) {
 | 
			
		||||
    // invarariant that the first unread message we find
 | 
			
		||||
    // does indeed satisfy our filter.
 | 
			
		||||
    if (fixture.unread_info.flavor === 'found') {
 | 
			
		||||
        _.each(fixture.all_messages, (msg) => {
 | 
			
		||||
        for (const msg of fixture.all_messages) {
 | 
			
		||||
            if (msg.id === fixture.unread_info.msg_id) {
 | 
			
		||||
                assert(filter.predicate()(msg));
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const muting_enabled = narrow_state.muting_enabled();
 | 
			
		||||
 
 | 
			
		||||
@@ -690,14 +690,14 @@ run_test('get_people_for_search_bar', () => {
 | 
			
		||||
    typeahead_helper.compare_by_pms = () => 0;
 | 
			
		||||
    message_store.user_ids = () => [];
 | 
			
		||||
 | 
			
		||||
    _.each(_.range(20), (i) => {
 | 
			
		||||
    for (const i of _.range(20)) {
 | 
			
		||||
        const person = {
 | 
			
		||||
            email: 'whatever@email.com',
 | 
			
		||||
            full_name: 'James Jones',
 | 
			
		||||
            user_id: 1000 + i,
 | 
			
		||||
        };
 | 
			
		||||
        people.add_in_realm(person);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const big_results = people.get_people_for_search_bar('James');
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -846,12 +846,12 @@ run_test('topic_suggestions', () => {
 | 
			
		||||
        topic_name: 'REXX',
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    _.each(['team', 'ignore', 'test'], function (topic_name) {
 | 
			
		||||
    for (const topic_name of ['team', 'ignore', 'test']) {
 | 
			
		||||
        topic_data.add_message({
 | 
			
		||||
            stream_id: office_id,
 | 
			
		||||
            topic_name: topic_name,
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    suggestions = get_suggestions('', 'te');
 | 
			
		||||
    expected = [
 | 
			
		||||
 
 | 
			
		||||
@@ -815,12 +815,12 @@ run_test('topic_suggestions', () => {
 | 
			
		||||
        topic_name: 'REXX',
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    _.each(['team', 'ignore', 'test'], function (topic_name) {
 | 
			
		||||
    for (const topic_name of ['team', 'ignore', 'test']) {
 | 
			
		||||
        topic_data.add_message({
 | 
			
		||||
            stream_id: office_id,
 | 
			
		||||
            topic_name: topic_name,
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    suggestions = search.get_suggestions_legacy('te');
 | 
			
		||||
    expected = [
 | 
			
		||||
 
 | 
			
		||||
@@ -8,9 +8,9 @@ let form_data;
 | 
			
		||||
 | 
			
		||||
const _jQuery = {
 | 
			
		||||
    each: function (lst, f) {
 | 
			
		||||
        _.each(lst, function (v, k) {
 | 
			
		||||
        for (const [k, v] of lst.entries()) {
 | 
			
		||||
            f(k, v);
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -641,11 +641,12 @@ run_test('on_events', () => {
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // Any of the blur_exceptions trigger blur event.
 | 
			
		||||
        _.each(blur_event_classes, function (class_name) {
 | 
			
		||||
        for (const class_name of blur_event_classes) {
 | 
			
		||||
            const handler = $(user_group_selector).get_on_handler("blur", class_name);
 | 
			
		||||
            const blur_exceptions = _.without([".pill-container", ".name", ".description", ".input", ".delete"],
 | 
			
		||||
                                              class_name);
 | 
			
		||||
            _.each(blur_exceptions, function (blur_exception) {
 | 
			
		||||
 | 
			
		||||
            for (const blur_exception of blur_exceptions) {
 | 
			
		||||
                api_endpoint_called = false;
 | 
			
		||||
                fake_this.closest = function (class_name) {
 | 
			
		||||
                    if (class_name === blur_exception || class_name === user_group_selector) {
 | 
			
		||||
@@ -655,7 +656,7 @@ run_test('on_events', () => {
 | 
			
		||||
                };
 | 
			
		||||
                handler.call(fake_this, event);
 | 
			
		||||
                assert(!api_endpoint_called);
 | 
			
		||||
            });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            api_endpoint_called = false;
 | 
			
		||||
            fake_this.closest = function (class_name) {
 | 
			
		||||
@@ -682,8 +683,7 @@ run_test('on_events', () => {
 | 
			
		||||
            handler.call(fake_this, event);
 | 
			
		||||
            assert(!api_endpoint_called);
 | 
			
		||||
            assert(settings_user_groups_reload_called);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }());
 | 
			
		||||
 | 
			
		||||
    (function test_update_cancel_button() {
 | 
			
		||||
 
 | 
			
		||||
@@ -883,12 +883,12 @@ run_test('is_subscriber_subset', () => {
 | 
			
		||||
        [bogus, bogus, false],
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    _.each(matrix, (row) => {
 | 
			
		||||
    for (const row of matrix) {
 | 
			
		||||
        assert.equal(
 | 
			
		||||
            stream_data.is_subscriber_subset(row[0], row[1]),
 | 
			
		||||
            row[2]
 | 
			
		||||
        );
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
run_test('invite_streams', () => {
 | 
			
		||||
 
 | 
			
		||||
@@ -65,9 +65,9 @@ run_test('filter_table', () => {
 | 
			
		||||
        },
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    _.each(sub_row_data, function (sub) {
 | 
			
		||||
    for (const sub of sub_row_data) {
 | 
			
		||||
        stream_data.add_sub(sub.name, sub);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let populated_subs;
 | 
			
		||||
 | 
			
		||||
@@ -79,7 +79,8 @@ run_test('filter_table', () => {
 | 
			
		||||
    subs.populate_stream_settings_left_panel();
 | 
			
		||||
 | 
			
		||||
    const sub_stubs = [];
 | 
			
		||||
    _.each(populated_subs, function (data) {
 | 
			
		||||
 | 
			
		||||
    for (const data of populated_subs) {
 | 
			
		||||
        const sub_row = ".stream-row-" + data.elem;
 | 
			
		||||
        sub_stubs.push(sub_row);
 | 
			
		||||
 | 
			
		||||
@@ -88,7 +89,7 @@ run_test('filter_table', () => {
 | 
			
		||||
        $(sub_row).detach = function () {
 | 
			
		||||
            return sub_row;
 | 
			
		||||
        };
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let tooltip_called = false;
 | 
			
		||||
    $(".tooltip").tooltip = function (obj) {
 | 
			
		||||
 
 | 
			
		||||
@@ -119,26 +119,29 @@ run_test('admin_default_streams_list', () => {
 | 
			
		||||
    const streams = ['devel', 'trac', 'zulip'];
 | 
			
		||||
 | 
			
		||||
    // When the logged in user is admin
 | 
			
		||||
    _.each(streams, function (stream) {
 | 
			
		||||
    for (const stream of streams) {
 | 
			
		||||
        const args = {
 | 
			
		||||
            stream: {name: stream, invite_only: false},
 | 
			
		||||
            can_modify: true,
 | 
			
		||||
        };
 | 
			
		||||
        html += render('admin_default_streams_list', args);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    html += "</table>";
 | 
			
		||||
    let span = $(html).find(".default_stream_name").first();
 | 
			
		||||
    assert.equal(span.text(), "devel");
 | 
			
		||||
 | 
			
		||||
    // When the logged in user is not admin
 | 
			
		||||
    html = '<table>';
 | 
			
		||||
    _.each(streams, function (stream) {
 | 
			
		||||
 | 
			
		||||
    for (const stream of streams) {
 | 
			
		||||
        const args = {
 | 
			
		||||
            stream: {name: stream, invite_only: false},
 | 
			
		||||
            can_modify: false,
 | 
			
		||||
        };
 | 
			
		||||
        html += render('admin_default_streams_list', args);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    html += "</table>";
 | 
			
		||||
    span = $(html).find(".default_stream_name").first();
 | 
			
		||||
    assert.equal(span.text(), "devel");
 | 
			
		||||
@@ -259,7 +262,8 @@ run_test('admin_invites_list', () => {
 | 
			
		||||
    let html = '<table>';
 | 
			
		||||
    const invites = ['alice', 'bob', 'carl'];
 | 
			
		||||
    let invite_id = 0;
 | 
			
		||||
    _.each(invites, function (invite) {
 | 
			
		||||
 | 
			
		||||
    for (const invite of invites) {
 | 
			
		||||
        const args = {
 | 
			
		||||
            invite: {
 | 
			
		||||
                email: invite + '@zulip.com',
 | 
			
		||||
@@ -271,7 +275,8 @@ run_test('admin_invites_list', () => {
 | 
			
		||||
        };
 | 
			
		||||
        html += render('admin_invites_list', args);
 | 
			
		||||
        invite_id += 1;
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    html += "</table>";
 | 
			
		||||
    const buttons = $(html).find('.button');
 | 
			
		||||
 | 
			
		||||
@@ -294,9 +299,11 @@ run_test('admin_tab', () => {
 | 
			
		||||
    const html = render('admin_tab', args);
 | 
			
		||||
    const admin_features = ["admin_users_table", "admin_bots_table",
 | 
			
		||||
                            "admin_deactivated_users_table", "admin_invites_table"];
 | 
			
		||||
    _.each(admin_features, function (admin_feature) {
 | 
			
		||||
 | 
			
		||||
    for (const admin_feature of admin_features) {
 | 
			
		||||
        assert.notEqual($(html).find("#" + admin_feature).length, 0);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    assert.equal($(html).find("input.admin-realm-name").val(), 'Zulip');
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -330,7 +337,7 @@ run_test('admin_user_list', () => {
 | 
			
		||||
    const users = ['alice', 'bob', 'carl'];
 | 
			
		||||
 | 
			
		||||
    // When the logged in user is admin
 | 
			
		||||
    _.each(users, function (user) {
 | 
			
		||||
    for (const user of users) {
 | 
			
		||||
        const args = {
 | 
			
		||||
            user: {
 | 
			
		||||
                is_active: true,
 | 
			
		||||
@@ -341,7 +348,8 @@ run_test('admin_user_list', () => {
 | 
			
		||||
            can_modify: true,
 | 
			
		||||
        };
 | 
			
		||||
        html += render('admin_user_list', args);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    html += "</table>";
 | 
			
		||||
 | 
			
		||||
    let buttons = $(html).find('.button');
 | 
			
		||||
@@ -354,7 +362,8 @@ run_test('admin_user_list', () => {
 | 
			
		||||
 | 
			
		||||
    // When the logged in user is not admin
 | 
			
		||||
    html = '<table>';
 | 
			
		||||
    _.each(users, function (user) {
 | 
			
		||||
 | 
			
		||||
    for (const user of users) {
 | 
			
		||||
        const args = {
 | 
			
		||||
            user: {
 | 
			
		||||
                is_active: true,
 | 
			
		||||
@@ -365,7 +374,8 @@ run_test('admin_user_list', () => {
 | 
			
		||||
            can_modify: false,
 | 
			
		||||
        };
 | 
			
		||||
        html += render('admin_user_list', args);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    html += "</table>";
 | 
			
		||||
 | 
			
		||||
    buttons = $(html).find('.button');
 | 
			
		||||
@@ -377,12 +387,14 @@ run_test('alert_word_settings_item', () => {
 | 
			
		||||
    let html = '<ul id="alert-words">';
 | 
			
		||||
    const words = ['lunch', 'support'];
 | 
			
		||||
    let args;
 | 
			
		||||
    _.each(words, function (word) {
 | 
			
		||||
 | 
			
		||||
    for (const word of words) {
 | 
			
		||||
        args = {
 | 
			
		||||
            word: word,
 | 
			
		||||
        };
 | 
			
		||||
        html += render('alert_word_settings_item', args);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    args = {
 | 
			
		||||
        word: '',
 | 
			
		||||
        editing: true,
 | 
			
		||||
@@ -410,7 +422,6 @@ run_test('alert_word_settings_item', () => {
 | 
			
		||||
    assert.equal(textbox.attr('class'), 'required');
 | 
			
		||||
    assert.equal(button.length, 1);
 | 
			
		||||
    assert.equal(button.text().trim(), 'translated: Add alert word');
 | 
			
		||||
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
run_test('all_messages_sidebar_actions', () => {
 | 
			
		||||
@@ -1140,9 +1151,9 @@ run_test('settings_tab', () => {
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // All checkboxes should be checked.
 | 
			
		||||
    _.each(checkbox_ids, function (checkbox) {
 | 
			
		||||
    for (const checkbox of checkbox_ids) {
 | 
			
		||||
        assert.equal($(html).find("#" + checkbox).is(":checked"), true);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Re-render with checkbox booleans set to false.
 | 
			
		||||
    for (const option of Object.keys(page_param_checkbox_options)) {
 | 
			
		||||
@@ -1152,9 +1163,9 @@ run_test('settings_tab', () => {
 | 
			
		||||
    html = render('settings_tab', {page_params: page_params});
 | 
			
		||||
 | 
			
		||||
    // All checkboxes should be unchecked.
 | 
			
		||||
    _.each(checkbox_ids, function (checkbox) {
 | 
			
		||||
    for (const checkbox of checkbox_ids) {
 | 
			
		||||
        assert.equal($(html).find("#" + checkbox).is(":checked"), false);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Check if enable_desktop_notifications setting disables subsetting too.
 | 
			
		||||
    const parent_elem = $('#pm_content_in_desktop_notifications_label').wrap("<div></div>");
 | 
			
		||||
@@ -1170,7 +1181,6 @@ run_test('settings_tab', () => {
 | 
			
		||||
        assert(!parent_elem.hasClass('control-label-disabled'));
 | 
			
		||||
        assert.equal($('#pm_content_in_desktop_notifications').attr('disabled'), undefined);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
run_test('stream_member_list_entry', () => {
 | 
			
		||||
@@ -1180,23 +1190,27 @@ run_test('stream_member_list_entry', () => {
 | 
			
		||||
    // First, as non-admin.
 | 
			
		||||
    let html = render('stream_member_list_entry',
 | 
			
		||||
                      {name: "King Hamlet", email: "hamlet@zulip.com"});
 | 
			
		||||
    _.each(everyone_items, function (item) {
 | 
			
		||||
 | 
			
		||||
    for (const item of everyone_items) {
 | 
			
		||||
        assert.equal($(html).find("." + item).length, 1);
 | 
			
		||||
    });
 | 
			
		||||
    _.each(admin_items, function (item) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (const item of admin_items) {
 | 
			
		||||
        assert.equal($(html).find("." + item).length, 0);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Now, as admin.
 | 
			
		||||
    html = render('stream_member_list_entry',
 | 
			
		||||
                  {name: "King Hamlet", email: "hamlet@zulip.com",
 | 
			
		||||
                   displaying_for_admin: true});
 | 
			
		||||
    _.each(everyone_items, function (item) {
 | 
			
		||||
 | 
			
		||||
    for (const item of everyone_items) {
 | 
			
		||||
        assert.equal($(html).find("." + item).length, 1);
 | 
			
		||||
    });
 | 
			
		||||
    _.each(admin_items, function (item) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (const item of admin_items) {
 | 
			
		||||
        assert.equal($(html).find("." + item).length, 1);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
run_test('stream_sidebar_actions', () => {
 | 
			
		||||
 
 | 
			
		||||
@@ -218,11 +218,11 @@ run_test('test_unread_logic', () => {
 | 
			
		||||
        { id: 20, topic: 'UNREAD2' },
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    _.each(msgs, (msg) => {
 | 
			
		||||
    for (const msg of msgs) {
 | 
			
		||||
        msg.type = 'stream';
 | 
			
		||||
        msg.stream_id = stream_id;
 | 
			
		||||
        msg.unread = true;
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    unread.process_loaded_messages(msgs);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -41,14 +41,14 @@ run_test('get_list_info w/real topic_data', () => {
 | 
			
		||||
        num_possible_topics: 0,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    _.each(_.range(7), (i) => {
 | 
			
		||||
    for (const i of _.range(7)) {
 | 
			
		||||
        const topic_name = 'topic ' + i;
 | 
			
		||||
        topic_data.add_message({
 | 
			
		||||
            stream_id: general.stream_id,
 | 
			
		||||
            topic_name: topic_name,
 | 
			
		||||
            message_id: 1000 + i,
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    narrow_state.topic = () => 'topic 6';
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -198,9 +198,9 @@ const matches = [
 | 
			
		||||
    zman,
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
_.each(matches, function (person) {
 | 
			
		||||
for (const person of matches) {
 | 
			
		||||
    global.people.add_in_realm(person);
 | 
			
		||||
});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function get_typeahead_result(query, current_stream, current_topic) {
 | 
			
		||||
    const result = th.sort_recipients(
 | 
			
		||||
 
 | 
			
		||||
@@ -60,11 +60,11 @@ const ignore_modules = [
 | 
			
		||||
    'unread_ui',
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
_.each(ignore_modules, (mod) => {
 | 
			
		||||
for (const mod of ignore_modules) {
 | 
			
		||||
    set_global(mod, {
 | 
			
		||||
        initialize: () => {},
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
emoji.emojis_by_name = new Map();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -69,9 +69,10 @@ class MarkdownComparer {
 | 
			
		||||
        // Sorts every attribute in every element by name.  Ensures consistent diff HTML output
 | 
			
		||||
 | 
			
		||||
        const attributeList = [];
 | 
			
		||||
        _.forEach(node.attributes, (attr) => {
 | 
			
		||||
 | 
			
		||||
        for (const attr of node.attributes) {
 | 
			
		||||
            attributeList.push(attr);
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // If put in above forEach loop, causes issues (possible nodes.attribute invalidation?)
 | 
			
		||||
        attributeList.forEach((attr) => {node.removeAttribute(attr.name);});
 | 
			
		||||
@@ -93,14 +94,14 @@ class MarkdownComparer {
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        if (node.hasChildNodes()) {
 | 
			
		||||
            _.forEach(node.children, (childNode) => {
 | 
			
		||||
            for (const childNode of node.children) {
 | 
			
		||||
                this._reorderAttributes(childNode);
 | 
			
		||||
            });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (node.content && node.content.hasChildNodes()) {
 | 
			
		||||
            _.forEach(node.content.children, (childNode) => {
 | 
			
		||||
            for (const childNode of node.content.children) {
 | 
			
		||||
                this._reorderAttributes(childNode);
 | 
			
		||||
            });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return node;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -30,9 +30,11 @@ exports.t = function (str, context) {
 | 
			
		||||
            suffix: key.slice(key.length - 2, key.length),
 | 
			
		||||
        };
 | 
			
		||||
    });
 | 
			
		||||
    _.each(substitutions, function (item) {
 | 
			
		||||
 | 
			
		||||
    for (const item of substitutions) {
 | 
			
		||||
        str = str.replace(item.prefix + item.keyword + item.suffix,
 | 
			
		||||
                          context[item.keyword]);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 'translated: ' + str;
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -80,7 +80,7 @@ exports.with_overrides = function (test_function) {
 | 
			
		||||
 | 
			
		||||
    test_function(override);
 | 
			
		||||
 | 
			
		||||
    _.each(clobber_callbacks, function (f) {
 | 
			
		||||
    for (const f of clobber_callbacks) {
 | 
			
		||||
        f();
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,3 @@
 | 
			
		||||
const _ = require('underscore/underscore.js');
 | 
			
		||||
 | 
			
		||||
// Stubs don't do any magical modifications to your namespace.  They
 | 
			
		||||
// just provide you a function that records what arguments get passed
 | 
			
		||||
// to it.  To use stubs as something more like "spies," use something
 | 
			
		||||
@@ -18,9 +16,9 @@ exports.make_stub = function () {
 | 
			
		||||
    self.get_args = function (...param_names) {
 | 
			
		||||
        const result = {};
 | 
			
		||||
 | 
			
		||||
        _.each(param_names, function (name, i) {
 | 
			
		||||
        for (const [i, name] of param_names.entries()) {
 | 
			
		||||
            result[name] = self.last_call_args[i];
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return result;
 | 
			
		||||
    };
 | 
			
		||||
 
 | 
			
		||||
@@ -99,7 +99,7 @@ exports.update_dom_with_unread_counts = function (counts) {
 | 
			
		||||
exports.process_loaded_messages = function (messages) {
 | 
			
		||||
    let need_resize = false;
 | 
			
		||||
 | 
			
		||||
    _.each(messages, function (message) {
 | 
			
		||||
    for (const message of messages) {
 | 
			
		||||
        const huddle_string = people.huddle_string(message);
 | 
			
		||||
 | 
			
		||||
        if (huddle_string) {
 | 
			
		||||
@@ -110,7 +110,7 @@ exports.process_loaded_messages = function (messages) {
 | 
			
		||||
                need_resize = true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    exports.update_huddles();
 | 
			
		||||
 | 
			
		||||
@@ -259,10 +259,10 @@ exports.update_huddles = function () {
 | 
			
		||||
    const html = render_group_pms({group_pms: group_pms});
 | 
			
		||||
    ui.get_content_element($('#group-pms')).html(html);
 | 
			
		||||
 | 
			
		||||
    _.each(huddles, function (user_ids_string) {
 | 
			
		||||
    for (const user_ids_string of huddles) {
 | 
			
		||||
        const count = unread.num_unread_for_person(user_ids_string);
 | 
			
		||||
        set_group_count(user_ids_string, count);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    show_huddles();
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,7 @@ exports.process_message = function (message) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _.each(exports.words, function (word) {
 | 
			
		||||
    for (const word of exports.words) {
 | 
			
		||||
        const clean = escape_user_regex(word);
 | 
			
		||||
        const before_punctuation = '\\s|^|>|[\\(\\".,\';\\[]';
 | 
			
		||||
        const after_punctuation = '\\s|$|<|[\\)\\"\\?!:.,\';\\]!]';
 | 
			
		||||
@@ -44,7 +44,7 @@ exports.process_message = function (message) {
 | 
			
		||||
            }
 | 
			
		||||
            return before + "<span class='alert-word'>" + word + "</span>" + after;
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.notifies = function (message) {
 | 
			
		||||
 
 | 
			
		||||
@@ -5,13 +5,15 @@ exports.render_alert_words_ui = function () {
 | 
			
		||||
    const word_list = $('#alert_words_list');
 | 
			
		||||
 | 
			
		||||
    word_list.find('.alert-word-item').remove();
 | 
			
		||||
    _.each(words, function (alert_word) {
 | 
			
		||||
 | 
			
		||||
    for (const alert_word of words) {
 | 
			
		||||
        const rendered_alert_word = render_alert_word_settings_item({
 | 
			
		||||
            word: alert_word,
 | 
			
		||||
            editing: false,
 | 
			
		||||
        });
 | 
			
		||||
        word_list.append(rendered_alert_word);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const new_alert_word_form = render_alert_word_settings_item({
 | 
			
		||||
        word: '',
 | 
			
		||||
        editing: true,
 | 
			
		||||
 
 | 
			
		||||
@@ -100,11 +100,11 @@ function render_attachments_ui() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function format_attachment_data(new_attachments) {
 | 
			
		||||
    _.each(new_attachments, function (attachment) {
 | 
			
		||||
    for (const attachment of new_attachments) {
 | 
			
		||||
        const time = new XDate(attachment.create_time);
 | 
			
		||||
        attachment.create_time_str = timerender.render_now(time).time_str;
 | 
			
		||||
        attachment.size_str = exports.bytes_to_size(attachment.size);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
exports.update_attachments = function (event) {
 | 
			
		||||
 
 | 
			
		||||
@@ -83,9 +83,10 @@ exports.get_services = function bot_data__get_services(bot_id) {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.initialize = function () {
 | 
			
		||||
    _.each(page_params.realm_bots, function (bot) {
 | 
			
		||||
    for (const bot of page_params.realm_bots) {
 | 
			
		||||
        exports.add(bot);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    delete page_params.realm_bots;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -339,11 +339,12 @@ exports.huddle_fraction_present = function (huddle) {
 | 
			
		||||
    const user_ids = huddle.split(',').map(s => parseInt(s, 10));
 | 
			
		||||
 | 
			
		||||
    let num_present = 0;
 | 
			
		||||
    _.each(user_ids, function (user_id) {
 | 
			
		||||
 | 
			
		||||
    for (const user_id of user_ids) {
 | 
			
		||||
        if (presence.is_active(user_id)) {
 | 
			
		||||
            num_present += 1;
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (num_present === user_ids.length) {
 | 
			
		||||
        return 1;
 | 
			
		||||
 
 | 
			
		||||
@@ -72,9 +72,9 @@ function buddy_list_create() {
 | 
			
		||||
        'height_to_fill',
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    _.each(func_names, function (func_name) {
 | 
			
		||||
    for (const func_name of func_names) {
 | 
			
		||||
        self[func_name] = conf[func_name];
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    self.keys = [];
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -122,11 +122,13 @@ exports.adjust_mac_shortcuts = function (key_elem_class, require_cmd_style) {
 | 
			
		||||
        if (key_text.indexOf('Ctrl') > -1 && require_cmd_style) {
 | 
			
		||||
            $(this).addClass("mac-cmd-key");
 | 
			
		||||
        }
 | 
			
		||||
        _.each(keys, function (key) {
 | 
			
		||||
 | 
			
		||||
        for (const key of keys) {
 | 
			
		||||
            if (keys_map.get(key)) {
 | 
			
		||||
                key_text = key_text.replace(key, keys_map.get(key));
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $(this).text(key_text);
 | 
			
		||||
    });
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -134,15 +134,15 @@ function update_user_row_when_fading(li, conf) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function display_users_normally(items, conf) {
 | 
			
		||||
    _.each(items, function (li) {
 | 
			
		||||
    for (const li of items) {
 | 
			
		||||
        conf.unfade(li);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function fade_users(items, conf) {
 | 
			
		||||
    _.each(items, function (li) {
 | 
			
		||||
    for (const li of items) {
 | 
			
		||||
        update_user_row_when_fading(li, conf);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function want_normal_display() {
 | 
			
		||||
@@ -232,12 +232,12 @@ exports.update_rendered_message_groups = function (message_groups, get_element)
 | 
			
		||||
    // This loop is superficially similar to some code in fade_messages, but an
 | 
			
		||||
    // important difference here is that we look at each message individually, whereas
 | 
			
		||||
    // the other code takes advantage of blocks beneath recipient bars.
 | 
			
		||||
    _.each(message_groups, function (message_group) {
 | 
			
		||||
    for (const message_group of message_groups) {
 | 
			
		||||
        const elt = get_element(message_group);
 | 
			
		||||
        const first_message = message_group.message_containers[0].msg;
 | 
			
		||||
        const should_fade = exports.should_fade_message(first_message);
 | 
			
		||||
        change_fade_state(elt, should_fade);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.initialize = function () {
 | 
			
		||||
 
 | 
			
		||||
@@ -28,12 +28,12 @@ exports.update_emoji_data = function () {
 | 
			
		||||
                is_realm_emoji: true,
 | 
			
		||||
            });
 | 
			
		||||
        } else {
 | 
			
		||||
            _.each(emoji_dict.aliases, function (alias) {
 | 
			
		||||
            for (const alias of emoji_dict.aliases) {
 | 
			
		||||
                exports.emoji_collection.push({
 | 
			
		||||
                    emoji_name: alias,
 | 
			
		||||
                    emoji_code: emoji_dict.emoji_code,
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -177,7 +177,7 @@ exports.show_message_expander = function (row) {
 | 
			
		||||
exports.condense_and_collapse = function (elems) {
 | 
			
		||||
    const height_cutoff = message_viewport.height() * 0.65;
 | 
			
		||||
 | 
			
		||||
    _.each(elems, function (elem) {
 | 
			
		||||
    for (const elem of elems) {
 | 
			
		||||
        const content = $(elem).find(".message_content");
 | 
			
		||||
        const message = current_msg_list.get(rows.id($(elem)));
 | 
			
		||||
        if (content !== undefined && message !== undefined) {
 | 
			
		||||
@@ -194,10 +194,10 @@ exports.condense_and_collapse = function (elems) {
 | 
			
		||||
            // specified whether this message should be expanded or condensed.
 | 
			
		||||
            if (message.condensed === true) {
 | 
			
		||||
                condense_row($(elem));
 | 
			
		||||
                return;
 | 
			
		||||
                continue;
 | 
			
		||||
            } else if (message.condensed === false) {
 | 
			
		||||
                uncondense_row($(elem));
 | 
			
		||||
                return;
 | 
			
		||||
                continue;
 | 
			
		||||
            } else if (long_message) {
 | 
			
		||||
                // By default, condense a long message.
 | 
			
		||||
                condense_row($(elem));
 | 
			
		||||
@@ -213,7 +213,7 @@ exports.condense_and_collapse = function (elems) {
 | 
			
		||||
                $(elem).find(".message_expander").show();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.initialize = function () {
 | 
			
		||||
 
 | 
			
		||||
@@ -40,11 +40,11 @@ exports.launch = function (conf) {
 | 
			
		||||
        'parent',
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    _.each(conf_fields, function (f) {
 | 
			
		||||
    for (const f of conf_fields) {
 | 
			
		||||
        if (!conf[f]) {
 | 
			
		||||
            blueslip.error('programmer omitted ' + f);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    conf.parent.append(confirm_dialog);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -109,9 +109,11 @@ function remove_div(div, ranges, selection) {
 | 
			
		||||
    window.setTimeout(function () {
 | 
			
		||||
        selection = window.getSelection();
 | 
			
		||||
        selection.removeAllRanges();
 | 
			
		||||
        _.each(ranges, function (range) {
 | 
			
		||||
 | 
			
		||||
        for (const range of ranges) {
 | 
			
		||||
            selection.addRange(range);
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $('#copytempdiv').remove();
 | 
			
		||||
    }, 0);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -257,7 +257,7 @@ exports.process_from_server = function process_from_server(messages) {
 | 
			
		||||
    const msgs_to_rerender = [];
 | 
			
		||||
    const non_echo_messages = [];
 | 
			
		||||
 | 
			
		||||
    _.each(messages, function (message) {
 | 
			
		||||
    for (const message of messages) {
 | 
			
		||||
        // In case we get the sent message before we get the send ACK, reify here
 | 
			
		||||
 | 
			
		||||
        const client_message = waiting_for_ack[message.local_id];
 | 
			
		||||
@@ -266,7 +266,7 @@ exports.process_from_server = function process_from_server(messages) {
 | 
			
		||||
            // the "main" codepath that doesn't have to id reconciliation.
 | 
			
		||||
            // We simply return non-echo messages to our caller.
 | 
			
		||||
            non_echo_messages.push(message);
 | 
			
		||||
            return;
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        exports.reify_message_id(message.local_id, message.id);
 | 
			
		||||
@@ -297,7 +297,7 @@ exports.process_from_server = function process_from_server(messages) {
 | 
			
		||||
 | 
			
		||||
        msgs_to_rerender.push(client_message);
 | 
			
		||||
        delete waiting_for_ack[client_message.id];
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (msgs_to_rerender.length > 0) {
 | 
			
		||||
        // In theory, we could just rerender messages where there were
 | 
			
		||||
@@ -326,9 +326,9 @@ exports.message_send_error = function message_send_error(local_id, error_respons
 | 
			
		||||
 | 
			
		||||
function abort_message(message) {
 | 
			
		||||
    // Remove in all lists in which it exists
 | 
			
		||||
    _.each([message_list.all, home_msg_list, current_msg_list], function (msg_list) {
 | 
			
		||||
    for (const msg_list of [message_list.all, home_msg_list, current_msg_list]) {
 | 
			
		||||
        msg_list.remove_and_rerender([message]);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
exports.initialize = function () {
 | 
			
		||||
 
 | 
			
		||||
@@ -45,8 +45,7 @@ exports.update_emojis = function update_emojis(realm_emojis) {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.initialize = function initialize() {
 | 
			
		||||
 | 
			
		||||
    _.each(emoji_codes.names, function (value) {
 | 
			
		||||
    for (const value of emoji_codes.names) {
 | 
			
		||||
        const base_name = emoji_codes.name_to_codepoint[value];
 | 
			
		||||
 | 
			
		||||
        if (exports.default_emoji_aliases.has(base_name)) {
 | 
			
		||||
@@ -54,7 +53,7 @@ exports.initialize = function initialize() {
 | 
			
		||||
        } else {
 | 
			
		||||
            exports.default_emoji_aliases.set(base_name, [value]);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    exports.update_emojis(page_params.realm_emoji);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -108,7 +108,8 @@ exports.generate_emoji_picker_data = function (realm_emojis) {
 | 
			
		||||
 | 
			
		||||
    for (const [category, codepoints] of Object.entries(emoji_codes.emoji_catalog)) {
 | 
			
		||||
        exports.complete_emoji_catalog[category] = [];
 | 
			
		||||
        _.each(codepoints, function (codepoint) {
 | 
			
		||||
 | 
			
		||||
        for (const codepoint of codepoints) {
 | 
			
		||||
            if (emoji_codes.codepoint_to_name.hasOwnProperty(codepoint)) {
 | 
			
		||||
                const emoji_dict = emoji.emojis_by_name.get(
 | 
			
		||||
                    emoji_codes.codepoint_to_name[codepoint]
 | 
			
		||||
@@ -117,18 +118,19 @@ exports.generate_emoji_picker_data = function (realm_emojis) {
 | 
			
		||||
                    exports.complete_emoji_catalog[category].push(emoji_dict);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    exports.complete_emoji_catalog.Popular = [];
 | 
			
		||||
    _.each(typeahead.popular_emojis, function (codepoint) {
 | 
			
		||||
 | 
			
		||||
    for (const codepoint of typeahead.popular_emojis) {
 | 
			
		||||
        if (emoji_codes.codepoint_to_name.hasOwnProperty(codepoint)) {
 | 
			
		||||
            const emoji_dict = emoji.emojis_by_name.get(emoji_codes.codepoint_to_name[codepoint]);
 | 
			
		||||
            if (emoji_dict !== undefined) {
 | 
			
		||||
                exports.complete_emoji_catalog.Popular.push(emoji_dict);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const categories = get_all_emoji_categories().filter(function (category) {
 | 
			
		||||
        return !!exports.complete_emoji_catalog[category.name];
 | 
			
		||||
@@ -206,12 +208,14 @@ function filter_emojis() {
 | 
			
		||||
        const categories = exports.complete_emoji_catalog;
 | 
			
		||||
        const search_terms = query.split(" ");
 | 
			
		||||
        search_results.length = 0;
 | 
			
		||||
        _.each(categories, function (category) {
 | 
			
		||||
 | 
			
		||||
        for (const category of categories) {
 | 
			
		||||
            if (category.name === "Popular") {
 | 
			
		||||
                return;
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            const emojis = category.emojis;
 | 
			
		||||
            _.each(emojis, function (emoji_dict) {
 | 
			
		||||
 | 
			
		||||
            for (const emoji_dict of emojis) {
 | 
			
		||||
                _.any(emoji_dict.aliases, function (alias) {
 | 
			
		||||
                    const match = _.every(search_terms, function (search_term) {
 | 
			
		||||
                        return alias.indexOf(search_term) >= 0;
 | 
			
		||||
@@ -221,8 +225,9 @@ function filter_emojis() {
 | 
			
		||||
                        return true;
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const rendered_search_results = render_emoji_popover_search_results({
 | 
			
		||||
            search_results: search_results,
 | 
			
		||||
            message_id: message_id,
 | 
			
		||||
 
 | 
			
		||||
@@ -37,14 +37,16 @@ function wrap_code(code) {
 | 
			
		||||
function wrap_quote(text) {
 | 
			
		||||
    const paragraphs = text.split('\n\n');
 | 
			
		||||
    const quoted_paragraphs = [];
 | 
			
		||||
 | 
			
		||||
    // Prefix each quoted paragraph with > at the
 | 
			
		||||
    // beginning of each line
 | 
			
		||||
    _.each(paragraphs, function (paragraph) {
 | 
			
		||||
    for (const paragraph of paragraphs) {
 | 
			
		||||
        const lines = paragraph.split('\n');
 | 
			
		||||
        quoted_paragraphs.push(_.map(
 | 
			
		||||
            _.reject(lines, function (line) { return line === ''; }),
 | 
			
		||||
            function (line) { return '> ' + line; }).join('\n'));
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return quoted_paragraphs.join('\n\n');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -166,10 +168,10 @@ exports.process_fenced_code = function (content) {
 | 
			
		||||
    const current_handler = default_hander();
 | 
			
		||||
    handler_stack.push(current_handler);
 | 
			
		||||
 | 
			
		||||
    _.each(input, function (line) {
 | 
			
		||||
    for (const line of input) {
 | 
			
		||||
        const handler = handler_stack[handler_stack.length - 1];
 | 
			
		||||
        handler.handle_line(line);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Clean up all trailing blocks by letting them
 | 
			
		||||
    // insert closing fences
 | 
			
		||||
 
 | 
			
		||||
@@ -260,7 +260,8 @@ Filter.parse = function (str) {
 | 
			
		||||
    if (matches === null) {
 | 
			
		||||
        return operators;
 | 
			
		||||
    }
 | 
			
		||||
    _.each(matches, function (token) {
 | 
			
		||||
 | 
			
		||||
    for (const token of matches) {
 | 
			
		||||
        let operator;
 | 
			
		||||
        const parts = token.split(':');
 | 
			
		||||
        if (token[0] === '"' || parts.length === 1) {
 | 
			
		||||
@@ -283,12 +284,13 @@ Filter.parse = function (str) {
 | 
			
		||||
            if (Filter.operator_to_prefix(operator, negated) === '') {
 | 
			
		||||
                // Put it as a search term, to not have duplicate operators
 | 
			
		||||
                search_term.push(token);
 | 
			
		||||
                return;
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            term = {negated: negated, operator: operator, operand: operand};
 | 
			
		||||
            operators.push(term);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // NB: Callers of 'parse' can assume that the 'search' operator is last.
 | 
			
		||||
    if (search_term.length > 0) {
 | 
			
		||||
        operator = 'search';
 | 
			
		||||
@@ -543,7 +545,7 @@ Filter.prototype = {
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    update_email: function (user_id, new_email) {
 | 
			
		||||
        _.each(this._operators, function (term) {
 | 
			
		||||
        for (const term of this._operators) {
 | 
			
		||||
            switch (term.operator) {
 | 
			
		||||
            case 'group-pm-with':
 | 
			
		||||
            case 'pm-with':
 | 
			
		||||
@@ -555,7 +557,7 @@ Filter.prototype = {
 | 
			
		||||
                    new_email
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Build a filter function from a list of operators.
 | 
			
		||||
 
 | 
			
		||||
@@ -209,11 +209,11 @@ exports.relevant_recipient_bars = function () {
 | 
			
		||||
        items[i].show_date = items[i].date_text !== items[i - 1].date_text;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _.each(items, function (item) {
 | 
			
		||||
    for (const item of items) {
 | 
			
		||||
        if (!item.need_frb) {
 | 
			
		||||
            delete item.date_html;
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return items;
 | 
			
		||||
};
 | 
			
		||||
@@ -303,9 +303,9 @@ exports.hide = function () {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.de_clutter_dates = function (items) {
 | 
			
		||||
    _.each(items, function (item) {
 | 
			
		||||
    for (const item of items) {
 | 
			
		||||
        item.elem.find('.recipient_row_date').toggle(item.show_date);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.update = function () {
 | 
			
		||||
 
 | 
			
		||||
@@ -93,7 +93,8 @@ exports.operators_to_hash = function (operators) {
 | 
			
		||||
 | 
			
		||||
    if (operators !== undefined) {
 | 
			
		||||
        hash = '#narrow';
 | 
			
		||||
        _.each(operators, function (elem) {
 | 
			
		||||
 | 
			
		||||
        for (const elem of operators) {
 | 
			
		||||
            // Support legacy tuples.
 | 
			
		||||
            const operator = elem.operator;
 | 
			
		||||
            const operand = elem.operand;
 | 
			
		||||
@@ -101,7 +102,7 @@ exports.operators_to_hash = function (operators) {
 | 
			
		||||
            const sign = elem.negated ? '-' : '';
 | 
			
		||||
            hash += '/' + sign + exports.encodeHashComponent(operator)
 | 
			
		||||
                  + '/' + exports.encode_operand(operator, operand);
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return hash;
 | 
			
		||||
 
 | 
			
		||||
@@ -236,9 +236,10 @@ exports.close_hotspot_icon = function (elem) {
 | 
			
		||||
 | 
			
		||||
function close_read_hotspots(new_hotspots) {
 | 
			
		||||
    const unwanted_hotspots = _.difference(_.keys(HOTSPOT_LOCATIONS), _.pluck(new_hotspots, 'name'));
 | 
			
		||||
    _.each(unwanted_hotspots, function (hotspot_name) {
 | 
			
		||||
 | 
			
		||||
    for (const hotspot_name of unwanted_hotspots) {
 | 
			
		||||
        exports.close_hotspot_icon($('#hotspot_' + hotspot_name + '_icon'));
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
exports.load_new = function (new_hotspots) {
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,7 @@ exports.set_up_toggler = function () {
 | 
			
		||||
        return modal;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    _.each(modals, function (modal) {
 | 
			
		||||
    for (const modal of modals) {
 | 
			
		||||
        keydown_util.handle({
 | 
			
		||||
            elem: modal,
 | 
			
		||||
            handlers: {
 | 
			
		||||
@@ -36,7 +36,7 @@ exports.set_up_toggler = function () {
 | 
			
		||||
                right_arrow: exports.toggler.maybe_go_right,
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $(".informational-overlays .overlay-tabs").append(elem);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -135,7 +135,8 @@ exports.add_topic_links = function (message) {
 | 
			
		||||
    }
 | 
			
		||||
    const topic = util.get_message_topic(message);
 | 
			
		||||
    let links = [];
 | 
			
		||||
    _.each(realm_filter_list, function (realm_filter) {
 | 
			
		||||
 | 
			
		||||
    for (const realm_filter of realm_filter_list) {
 | 
			
		||||
        const pattern = realm_filter[0];
 | 
			
		||||
        const url = realm_filter[1];
 | 
			
		||||
        let match;
 | 
			
		||||
@@ -152,7 +153,7 @@ exports.add_topic_links = function (message) {
 | 
			
		||||
            }
 | 
			
		||||
            links.push(link_url);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Also make raw urls navigable
 | 
			
		||||
    const url_re = /\b(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/g; // Slightly modified from third/marked.js
 | 
			
		||||
@@ -233,11 +234,12 @@ function handleRealmFilter(pattern, matches) {
 | 
			
		||||
    let url = realm_filter_map[pattern];
 | 
			
		||||
 | 
			
		||||
    let current_group = 1;
 | 
			
		||||
    _.each(matches, function (match) {
 | 
			
		||||
 | 
			
		||||
    for (const match of matches) {
 | 
			
		||||
        const back_ref = "\\" + current_group;
 | 
			
		||||
        url = url.replace(back_ref, match);
 | 
			
		||||
        current_group += 1;
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return url;
 | 
			
		||||
}
 | 
			
		||||
@@ -281,11 +283,13 @@ function python_to_js_filter(pattern, url) {
 | 
			
		||||
    // flags, so keep those and ignore the rest
 | 
			
		||||
    if (match) {
 | 
			
		||||
        const py_flags = match[1].split("");
 | 
			
		||||
        _.each(py_flags, function (flag) {
 | 
			
		||||
 | 
			
		||||
        for (const flag of py_flags) {
 | 
			
		||||
            if ("im".indexOf(flag) !== -1) {
 | 
			
		||||
                js_flags += flag;
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        pattern = pattern.replace(inline_flag_re, "");
 | 
			
		||||
    }
 | 
			
		||||
    // Ideally we should have been checking that realm filters
 | 
			
		||||
@@ -315,19 +319,20 @@ exports.set_realm_filters = function (realm_filters) {
 | 
			
		||||
    realm_filter_list = [];
 | 
			
		||||
 | 
			
		||||
    const marked_rules = [];
 | 
			
		||||
    _.each(realm_filters, function (realm_filter) {
 | 
			
		||||
 | 
			
		||||
    for (const realm_filter of realm_filters) {
 | 
			
		||||
        const pattern = realm_filter[0];
 | 
			
		||||
        const url = realm_filter[1];
 | 
			
		||||
        const js_filters = python_to_js_filter(pattern, url);
 | 
			
		||||
        if (!js_filters[0]) {
 | 
			
		||||
            // Skip any realm filters that could not be converted
 | 
			
		||||
            return;
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        realm_filter_map[js_filters[0]] = js_filters[1];
 | 
			
		||||
        realm_filter_list.push([js_filters[0], js_filters[1]]);
 | 
			
		||||
        marked_rules.push(js_filters[0]);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    marked.InlineLexer.rules.zulip.realm_filters = marked_rules;
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -648,7 +648,8 @@ exports.show_history = function (message) {
 | 
			
		||||
        success: function (data) {
 | 
			
		||||
            const content_edit_history = [];
 | 
			
		||||
            let prev_timestamp;
 | 
			
		||||
            _.each(data.message_history, function (msg, index) {
 | 
			
		||||
 | 
			
		||||
            for (const [index, msg] of data.message_history.entries()) {
 | 
			
		||||
                // Format timestamp nicely for display
 | 
			
		||||
                const timestamp = timerender.get_full_time(msg.timestamp);
 | 
			
		||||
                const item = {
 | 
			
		||||
@@ -685,7 +686,7 @@ exports.show_history = function (message) {
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                content_edit_history.push(item);
 | 
			
		||||
            });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $('#message-history').html(render_message_edit_history({
 | 
			
		||||
                edited_messages: content_edit_history,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,9 @@
 | 
			
		||||
function maybe_add_narrowed_messages(messages, msg_list) {
 | 
			
		||||
    const ids = [];
 | 
			
		||||
    _.each(messages, function (elem) {
 | 
			
		||||
 | 
			
		||||
    for (const elem of messages) {
 | 
			
		||||
        ids.push(elem.id);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    channel.get({
 | 
			
		||||
        url: '/json/messages/matches_narrow',
 | 
			
		||||
@@ -17,14 +18,15 @@ function maybe_add_narrowed_messages(messages, msg_list) {
 | 
			
		||||
 | 
			
		||||
            let new_messages = [];
 | 
			
		||||
            const elsewhere_messages = [];
 | 
			
		||||
            _.each(messages, function (elem) {
 | 
			
		||||
 | 
			
		||||
            for (const elem of messages) {
 | 
			
		||||
                if (data.messages.hasOwnProperty(elem.id)) {
 | 
			
		||||
                    util.set_match_data(elem, data.messages[elem.id]);
 | 
			
		||||
                    new_messages.push(elem);
 | 
			
		||||
                } else {
 | 
			
		||||
                    elsewhere_messages.push(elem);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // This second call to add_message_metadata in the
 | 
			
		||||
            // insert_new_messages code path helps in very rare race
 | 
			
		||||
@@ -105,10 +107,10 @@ exports.update_messages = function update_messages(events) {
 | 
			
		||||
    let changed_compose = false;
 | 
			
		||||
    let message_content_edited = false;
 | 
			
		||||
 | 
			
		||||
    _.each(events, function (event) {
 | 
			
		||||
    for (const event of events) {
 | 
			
		||||
        const msg = message_store.get(event.message_id);
 | 
			
		||||
        if (msg === undefined) {
 | 
			
		||||
            return;
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        delete msg.local_edit_timestamp;
 | 
			
		||||
@@ -176,10 +178,10 @@ exports.update_messages = function update_messages(events) {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            _.each(event.message_ids, function (id) {
 | 
			
		||||
            for (const id of event.message_ids) {
 | 
			
		||||
                const msg = message_store.get(id);
 | 
			
		||||
                if (msg === undefined) {
 | 
			
		||||
                    return;
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Remove the recent topics entry for the old topics;
 | 
			
		||||
@@ -215,7 +217,7 @@ exports.update_messages = function update_messages(events) {
 | 
			
		||||
                        current_msg_list.remove_and_rerender([{id: id}]);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (event.orig_content !== undefined) {
 | 
			
		||||
@@ -249,7 +251,7 @@ exports.update_messages = function update_messages(events) {
 | 
			
		||||
 | 
			
		||||
        notifications.received_messages([msg]);
 | 
			
		||||
        alert_words.process_message(msg);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // If a topic was edited, we re-render the whole view to get any
 | 
			
		||||
    // propagated edits to be updated (since the topic edits can have
 | 
			
		||||
 
 | 
			
		||||
@@ -194,11 +194,11 @@ MessageListData.prototype = {
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    update_user_full_name: function (user_id, full_name) {
 | 
			
		||||
        _.each(this._items, function (item) {
 | 
			
		||||
        for (const item of this._items) {
 | 
			
		||||
            if (item.sender_id && item.sender_id === user_id) {
 | 
			
		||||
                item.sender_full_name = full_name;
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    update_user_avatar: function (user_id, avatar_url) {
 | 
			
		||||
@@ -206,20 +206,20 @@ MessageListData.prototype = {
 | 
			
		||||
        // We may want to de-dup some logic with update_user_full_name,
 | 
			
		||||
        // especially if we want to optimize this with some kind of
 | 
			
		||||
        // hash that maps sender_id -> messages.
 | 
			
		||||
        _.each(this._items, function (item) {
 | 
			
		||||
        for (const item of this._items) {
 | 
			
		||||
            if (item.sender_id && item.sender_id === user_id) {
 | 
			
		||||
                item.small_avatar_url = avatar_url;
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    update_stream_name: function (stream_id, new_stream_name) {
 | 
			
		||||
        _.each(this._items, function (item) {
 | 
			
		||||
        for (const item of this._items) {
 | 
			
		||||
            if (item.stream_id && item.stream_id === stream_id) {
 | 
			
		||||
                item.display_recipient = new_stream_name;
 | 
			
		||||
                item.stream = new_stream_name;
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    add_messages: function (messages) {
 | 
			
		||||
@@ -240,7 +240,7 @@ MessageListData.prototype = {
 | 
			
		||||
            // that fail our filter predicate
 | 
			
		||||
            messages = self.valid_non_duplicated_messages(messages);
 | 
			
		||||
 | 
			
		||||
            _.each(messages, function (msg) {
 | 
			
		||||
            for (const msg of messages) {
 | 
			
		||||
                // Put messages in correct order on either side of the
 | 
			
		||||
                // message list.  This code path assumes that messages
 | 
			
		||||
                // is a (1) sorted, and (2) consecutive block of
 | 
			
		||||
@@ -253,7 +253,7 @@ MessageListData.prototype = {
 | 
			
		||||
                } else {
 | 
			
		||||
                    interior_messages.push(msg);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (interior_messages.length > 0) {
 | 
			
		||||
@@ -330,18 +330,21 @@ MessageListData.prototype = {
 | 
			
		||||
 | 
			
		||||
    remove: function (messages) {
 | 
			
		||||
        const self = this;
 | 
			
		||||
        _.each(messages, function (message) {
 | 
			
		||||
 | 
			
		||||
        for (const message of messages) {
 | 
			
		||||
            const stored_message = self._hash[message.id];
 | 
			
		||||
            if (stored_message !== undefined) {
 | 
			
		||||
                delete self._hash[stored_message];
 | 
			
		||||
            }
 | 
			
		||||
            delete self._local_only[message.id];
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const msg_ids_to_remove = {};
 | 
			
		||||
        _.each(messages, function (message) {
 | 
			
		||||
 | 
			
		||||
        for (const message of messages) {
 | 
			
		||||
            msg_ids_to_remove[message.id] = true;
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._items = _.filter(this._items, function (message) {
 | 
			
		||||
            return !msg_ids_to_remove.hasOwnProperty(message.id);
 | 
			
		||||
        });
 | 
			
		||||
@@ -426,15 +429,15 @@ MessageListData.prototype = {
 | 
			
		||||
            potential_closest_matches.unshift(_.last(potential_closest_matches) - 1);
 | 
			
		||||
            let best_match = items[closest].id;
 | 
			
		||||
 | 
			
		||||
            _.each(potential_closest_matches, function (potential_idx) {
 | 
			
		||||
            for (const potential_idx of potential_closest_matches) {
 | 
			
		||||
                if (potential_idx < 0) {
 | 
			
		||||
                    return;
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                const item = items[potential_idx];
 | 
			
		||||
 | 
			
		||||
                if (item === undefined) {
 | 
			
		||||
                    blueslip.warn('Invalid potential_idx: ' + potential_idx);
 | 
			
		||||
                    return;
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                const potential_match = item.id;
 | 
			
		||||
@@ -443,7 +446,7 @@ MessageListData.prototype = {
 | 
			
		||||
                    best_match = potential_match;
 | 
			
		||||
                    closest = potential_idx;
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return items[closest].id;
 | 
			
		||||
    },
 | 
			
		||||
@@ -457,9 +460,9 @@ MessageListData.prototype = {
 | 
			
		||||
 | 
			
		||||
        const id_set = {};
 | 
			
		||||
 | 
			
		||||
        _.each(msg_ids, function (msg_id) {
 | 
			
		||||
        for (const msg_id of msg_ids) {
 | 
			
		||||
            id_set[msg_id] = true;
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let idx = this.selected_idx() + 1;
 | 
			
		||||
        while (idx < this._items.length) {
 | 
			
		||||
 
 | 
			
		||||
@@ -288,7 +288,7 @@ MessageListView.prototype = {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        _.each(message_containers, function (message_container) {
 | 
			
		||||
        for (const message_container of message_containers) {
 | 
			
		||||
            const message_reactions = reactions.get_message_reactions(message_container.msg);
 | 
			
		||||
            message_container.msg.message_reactions = message_reactions;
 | 
			
		||||
            message_container.include_recipient = false;
 | 
			
		||||
@@ -359,7 +359,7 @@ MessageListView.prototype = {
 | 
			
		||||
            self._add_msg_edited_vars(message_container);
 | 
			
		||||
 | 
			
		||||
            prev = message_container;
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        finish_group();
 | 
			
		||||
 | 
			
		||||
@@ -513,11 +513,12 @@ MessageListView.prototype = {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const self = this;
 | 
			
		||||
        _.each($message_rows, function (dom_row) {
 | 
			
		||||
 | 
			
		||||
        for (const dom_row of $message_rows) {
 | 
			
		||||
            const row = $(dom_row);
 | 
			
		||||
            self._put_row(row);
 | 
			
		||||
            self._post_process_single_row(row);
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _post_process_single_row: function (row) {
 | 
			
		||||
@@ -721,9 +722,9 @@ MessageListView.prototype = {
 | 
			
		||||
        let last_message_row;
 | 
			
		||||
        let last_group_row;
 | 
			
		||||
 | 
			
		||||
        _.each(message_containers, function (message_container) {
 | 
			
		||||
        for (const message_container of message_containers) {
 | 
			
		||||
            self.message_containers[message_container.msg.id] = message_container;
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Render new message groups on the top
 | 
			
		||||
        if (message_actions.prepend_groups.length > 0) {
 | 
			
		||||
@@ -751,7 +752,7 @@ MessageListView.prototype = {
 | 
			
		||||
        if (message_actions.rerender_groups.length > 0) {
 | 
			
		||||
            save_scroll_position();
 | 
			
		||||
 | 
			
		||||
            _.each(message_actions.rerender_groups, function (message_group) {
 | 
			
		||||
            for (const message_group of message_actions.rerender_groups) {
 | 
			
		||||
                const old_message_group = $('#' + message_group.message_group_id);
 | 
			
		||||
                // Remove the top date_row, we'll re-add it after rendering
 | 
			
		||||
                old_message_group.prev('.date_row').remove();
 | 
			
		||||
@@ -768,7 +769,7 @@ MessageListView.prototype = {
 | 
			
		||||
                self._post_process(dom_messages);
 | 
			
		||||
                old_message_group.replaceWith(rendered_groups);
 | 
			
		||||
                condense.condense_and_collapse(dom_messages);
 | 
			
		||||
            });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Update the rendering for message rows which used to be last
 | 
			
		||||
@@ -780,11 +781,12 @@ MessageListView.prototype = {
 | 
			
		||||
        // class doesn't do anything.
 | 
			
		||||
        if (message_actions.rerender_messages_next_same_sender.length > 0) {
 | 
			
		||||
            const targets = message_actions.rerender_messages_next_same_sender;
 | 
			
		||||
            _.each(targets, function (message_container) {
 | 
			
		||||
 | 
			
		||||
            for (const message_container of targets) {
 | 
			
		||||
                const row = self.get_row(message_container.msg.id);
 | 
			
		||||
                $(row).find("div.messagebox").toggleClass("next_is_same_sender",
 | 
			
		||||
                                                          message_container.next_is_same_sender);
 | 
			
		||||
            });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Insert new messages in to the last message group
 | 
			
		||||
@@ -917,14 +919,14 @@ MessageListView.prototype = {
 | 
			
		||||
        let id_of_last_message_sent_by_us = -1;
 | 
			
		||||
 | 
			
		||||
        // C++ iterators would have made this less painful
 | 
			
		||||
        _.each(rendered_elems.reverse(), function (elem) {
 | 
			
		||||
        for (const elem of rendered_elems.reverse()) {
 | 
			
		||||
            // Sometimes there are non-DOM elements in rendered_elems; only
 | 
			
		||||
            // try to get the heights of actual trs.
 | 
			
		||||
            if (elem.is("div")) {
 | 
			
		||||
                new_messages_height += elem.height();
 | 
			
		||||
                // starting from the last message, ignore message heights that weren't sent by me.
 | 
			
		||||
                if (id_of_last_message_sent_by_us > -1) {
 | 
			
		||||
                    return;
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                const row_id = rows.id(elem);
 | 
			
		||||
                // check for `row_id` NaN in case we're looking at a date row or bookend row
 | 
			
		||||
@@ -933,7 +935,7 @@ MessageListView.prototype = {
 | 
			
		||||
                    id_of_last_message_sent_by_us = rows.id(elem);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }, this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return new_messages_height;
 | 
			
		||||
    },
 | 
			
		||||
@@ -1241,7 +1243,8 @@ MessageListView.prototype = {
 | 
			
		||||
 | 
			
		||||
        const message_groups = [];
 | 
			
		||||
        let current_group = [];
 | 
			
		||||
        _.each(message_containers, function (message_container) {
 | 
			
		||||
 | 
			
		||||
        for (const message_container of message_containers) {
 | 
			
		||||
            if (current_group.length === 0 ||
 | 
			
		||||
                same_recipient(current_group[current_group.length - 1], message_container)) {
 | 
			
		||||
                current_group.push(message_container);
 | 
			
		||||
@@ -1250,13 +1253,15 @@ MessageListView.prototype = {
 | 
			
		||||
                current_group = [];
 | 
			
		||||
            }
 | 
			
		||||
            self._rerender_message(message_container, message_content_edited);
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (current_group.length !== 0) {
 | 
			
		||||
            message_groups.push(current_group);
 | 
			
		||||
        }
 | 
			
		||||
        _.each(message_groups, function (messages_in_group) {
 | 
			
		||||
 | 
			
		||||
        for (const messages_in_group of message_groups) {
 | 
			
		||||
            self._rerender_header(messages_in_group, message_content_edited);
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    append: function (messages, messages_are_new) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,31 +1,31 @@
 | 
			
		||||
exports.update_stream_name = function (stream_id, new_name) {
 | 
			
		||||
    _.each([home_msg_list, message_list.narrowed, message_list.all], function (list) {
 | 
			
		||||
    for (const list of [home_msg_list, message_list.narrowed, message_list.all]) {
 | 
			
		||||
        if (list === undefined) {
 | 
			
		||||
            return;
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        list.update_stream_name(stream_id, new_name);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.update_user_full_name = function (user_id, full_name) {
 | 
			
		||||
    _.each([home_msg_list, message_list.narrowed, message_list.all], function (list) {
 | 
			
		||||
    for (const list of [home_msg_list, message_list.narrowed, message_list.all]) {
 | 
			
		||||
        if (list === undefined) {
 | 
			
		||||
            return;
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        list.update_user_full_name(user_id, full_name);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.update_avatar = function (user_id, avatar_url) {
 | 
			
		||||
    let url = avatar_url;
 | 
			
		||||
    url = people.format_small_avatar_url(url);
 | 
			
		||||
 | 
			
		||||
    _.each([home_msg_list, message_list.narrowed, message_list.all], function (list) {
 | 
			
		||||
    for (const list of [home_msg_list, message_list.narrowed, message_list.all]) {
 | 
			
		||||
        if (list === undefined) {
 | 
			
		||||
            return;
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        list.update_user_avatar(user_id, url);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
window.message_live_update = exports;
 | 
			
		||||
 
 | 
			
		||||
@@ -67,9 +67,9 @@ exports.process_message_for_recent_private_messages = function (message) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _.each(user_ids, function (user_id) {
 | 
			
		||||
    for (const user_id of user_ids) {
 | 
			
		||||
        pm_conversations.set_partner(user_id);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pm_conversations.recent.insert(user_ids, message.id);
 | 
			
		||||
};
 | 
			
		||||
@@ -176,9 +176,9 @@ exports.add_message_metadata = function (message) {
 | 
			
		||||
        exports.process_message_for_recent_private_messages(message);
 | 
			
		||||
 | 
			
		||||
        if (people.is_my_user_id(message.sender_id)) {
 | 
			
		||||
            _.each(message.display_recipient, (recip) => {
 | 
			
		||||
            for (const recip of message.display_recipient) {
 | 
			
		||||
                message_user_ids.add(recip.id);
 | 
			
		||||
            });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
@@ -202,7 +202,7 @@ exports.reify_message_id = function (opts) {
 | 
			
		||||
        stored_messages.delete(old_id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _.each([message_list.all, home_msg_list, message_list.narrowed], function (msg_list) {
 | 
			
		||||
    for (const msg_list of [message_list.all, home_msg_list, message_list.narrowed]) {
 | 
			
		||||
        if (msg_list !== undefined) {
 | 
			
		||||
            msg_list.change_message_id(old_id, new_id);
 | 
			
		||||
 | 
			
		||||
@@ -210,7 +210,7 @@ exports.reify_message_id = function (opts) {
 | 
			
		||||
                msg_list.view.change_message_id(old_id, new_id);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
window.message_store = exports;
 | 
			
		||||
 
 | 
			
		||||
@@ -40,7 +40,7 @@ exports.get_muted_topics = function () {
 | 
			
		||||
exports.set_muted_topics = function (tuples) {
 | 
			
		||||
    muted_topics.clear();
 | 
			
		||||
 | 
			
		||||
    _.each(tuples, function (tuple) {
 | 
			
		||||
    for (const tuple of tuples) {
 | 
			
		||||
        const stream_name = tuple[0];
 | 
			
		||||
        const topic = tuple[1];
 | 
			
		||||
 | 
			
		||||
@@ -48,11 +48,11 @@ exports.set_muted_topics = function (tuples) {
 | 
			
		||||
 | 
			
		||||
        if (!stream_id) {
 | 
			
		||||
            blueslip.warn('Unknown stream in set_muted_topics: ' + stream_name);
 | 
			
		||||
            return;
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        exports.add_muted_topic(stream_id, topic);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.initialize = function () {
 | 
			
		||||
 
 | 
			
		||||
@@ -70,7 +70,8 @@ exports.update_muted_topics = function (muted_topics) {
 | 
			
		||||
exports.set_up_muted_topics_ui = function (muted_topics) {
 | 
			
		||||
    const muted_topics_table = $("#muted_topics_table tbody");
 | 
			
		||||
    muted_topics_table.empty();
 | 
			
		||||
    _.each(muted_topics, function (tup) {
 | 
			
		||||
 | 
			
		||||
    for (const tup of muted_topics) {
 | 
			
		||||
        const stream_id = tup[0];
 | 
			
		||||
        const topic = tup[1];
 | 
			
		||||
 | 
			
		||||
@@ -78,7 +79,7 @@ exports.set_up_muted_topics_ui = function (muted_topics) {
 | 
			
		||||
 | 
			
		||||
        if (!stream) {
 | 
			
		||||
            blueslip.warn('Unknown stream_id in set_up_muted_topics_ui: ' + stream_id);
 | 
			
		||||
            return;
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const template_data = {
 | 
			
		||||
@@ -89,7 +90,7 @@ exports.set_up_muted_topics_ui = function (muted_topics) {
 | 
			
		||||
 | 
			
		||||
        const row = render_muted_topic_ui_row(template_data);
 | 
			
		||||
        muted_topics_table.append(row);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.mute = function (stream_id, topic) {
 | 
			
		||||
 
 | 
			
		||||
@@ -296,10 +296,11 @@ exports.activate = function (raw_operators, opts) {
 | 
			
		||||
 | 
			
		||||
    if (page_params.search_pills_enabled && opts.trigger !== 'search') {
 | 
			
		||||
        search_pill_widget.widget.clear(true);
 | 
			
		||||
        _.each(operators, function (operator) {
 | 
			
		||||
 | 
			
		||||
        for (const operator of operators) {
 | 
			
		||||
            const search_string = Filter.unparse([operator]);
 | 
			
		||||
            search_pill.append_search_string(search_string, search_pill_widget.widget);
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (filter.contains_only_private_messages()) {
 | 
			
		||||
@@ -887,7 +888,7 @@ function show_search_query() {
 | 
			
		||||
        search_string_display.append($('<span>').text(stream_topic_string));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _.each(query_words, function (query_word) {
 | 
			
		||||
    for (const query_word of query_words) {
 | 
			
		||||
        search_string_display.append(' ');
 | 
			
		||||
 | 
			
		||||
        // if query contains stop words, it is enclosed by a <del> tag
 | 
			
		||||
@@ -899,7 +900,7 @@ function show_search_query() {
 | 
			
		||||
            // We use .text("...") to sanitize the user-given query_string.
 | 
			
		||||
            search_string_display.append($('<span>').text(query_word));
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (query_contains_stop_words) {
 | 
			
		||||
        search_string_display.html(i18n.t(
 | 
			
		||||
 
 | 
			
		||||
@@ -49,7 +49,8 @@ exports.search_string = function () {
 | 
			
		||||
function collect_single(operators) {
 | 
			
		||||
    const seen   = new Dict();
 | 
			
		||||
    const result = new Dict();
 | 
			
		||||
    _.each(operators, function (elem) {
 | 
			
		||||
 | 
			
		||||
    for (const elem of operators) {
 | 
			
		||||
        const key = elem.operator;
 | 
			
		||||
        if (seen.has(key)) {
 | 
			
		||||
            result.delete(key);
 | 
			
		||||
@@ -57,7 +58,8 @@ function collect_single(operators) {
 | 
			
		||||
            result.set(key, elem.operand);
 | 
			
		||||
            seen.set(key, true);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -578,13 +578,13 @@ exports.request_desktop_notifications_permission = function () {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.received_messages = function (messages) {
 | 
			
		||||
    _.each(messages, function (message) {
 | 
			
		||||
    for (const message of messages) {
 | 
			
		||||
        if (!exports.message_is_notifiable(message)) {
 | 
			
		||||
            return;
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        if (!unread.message_unread(message)) {
 | 
			
		||||
            // The message is already read; Zulip is currently in focus.
 | 
			
		||||
            return;
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        message.notification_sent = true;
 | 
			
		||||
@@ -598,7 +598,7 @@ exports.received_messages = function (messages) {
 | 
			
		||||
        if (exports.should_send_audible_notification(message) && supports_sound) {
 | 
			
		||||
            $("#notifications-area").find("audio")[0].play();
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function get_message_header(message) {
 | 
			
		||||
@@ -656,13 +656,13 @@ exports.notify_local_mixes = function (messages, need_user_to_scroll) {
 | 
			
		||||
        sent_messages.messages here.
 | 
			
		||||
    */
 | 
			
		||||
 | 
			
		||||
    _.each(messages, function (message) {
 | 
			
		||||
    for (const message of messages) {
 | 
			
		||||
        if (!people.is_my_user_id(message.sender_id)) {
 | 
			
		||||
            // This can happen if the client is offline for a while
 | 
			
		||||
            // around the time this client sends a message; see the
 | 
			
		||||
            // caller of message_events.insert_new_messages.
 | 
			
		||||
            blueslip.info('Slightly unexpected: A message not sent by us batches with those that were.');
 | 
			
		||||
            return;
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let reason = exports.get_local_notify_mix_reason(message);
 | 
			
		||||
@@ -678,7 +678,7 @@ exports.notify_local_mixes = function (messages, need_user_to_scroll) {
 | 
			
		||||
 | 
			
		||||
            // This is the HAPPY PATH--for most messages we do nothing
 | 
			
		||||
            // other than maybe sending the above message.
 | 
			
		||||
            return;
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const link_msg_id = message.id;
 | 
			
		||||
@@ -687,15 +687,15 @@ exports.notify_local_mixes = function (messages, need_user_to_scroll) {
 | 
			
		||||
                                 {message_recipient: get_message_header(message)});
 | 
			
		||||
 | 
			
		||||
        exports.notify_above_composebox(reason, link_class, link_msg_id, link_text);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// for callback when we have to check with the server if a message should be in
 | 
			
		||||
// the current_msg_list (!can_apply_locally; a.k.a. "a search").
 | 
			
		||||
exports.notify_messages_outside_current_search = function (messages) {
 | 
			
		||||
    _.each(messages, function (message) {
 | 
			
		||||
    for (const message of messages) {
 | 
			
		||||
        if (!people.is_current_user(message.sender_email)) {
 | 
			
		||||
            return;
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        const link_text = i18n.t("Narrow to __- message_recipient__",
 | 
			
		||||
                                 {message_recipient: get_message_header(message)});
 | 
			
		||||
@@ -703,7 +703,7 @@ exports.notify_messages_outside_current_search = function (messages) {
 | 
			
		||||
                                        "compose_notification_narrow_by_topic",
 | 
			
		||||
                                        message.id,
 | 
			
		||||
                                        link_text);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.clear_compose_notifications = function () {
 | 
			
		||||
@@ -718,14 +718,14 @@ exports.reify_message_id = function (opts) {
 | 
			
		||||
 | 
			
		||||
    // If a message ID that we're currently storing (as a link) has changed,
 | 
			
		||||
    // update that link as well
 | 
			
		||||
    _.each($('#out-of-view-notification a'), function (e) {
 | 
			
		||||
    for (const e of $('#out-of-view-notification a')) {
 | 
			
		||||
        const elem = $(e);
 | 
			
		||||
        const message_id = elem.data('message-id');
 | 
			
		||||
 | 
			
		||||
        if (message_id === old_id) {
 | 
			
		||||
            elem.data('message-id', new_id);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.register_click_handlers = function () {
 | 
			
		||||
 
 | 
			
		||||
@@ -861,11 +861,11 @@ exports.filter_people_by_search_terms = function (users, search_terms) {
 | 
			
		||||
 | 
			
		||||
    // Loop through users and populate filtered_users only
 | 
			
		||||
    // if they include search_terms
 | 
			
		||||
    _.each(users, function (user) {
 | 
			
		||||
    for (const user of users) {
 | 
			
		||||
        const person = exports.get_by_email(user.email);
 | 
			
		||||
        // Get person object (and ignore errors)
 | 
			
		||||
        if (!person || !person.full_name) {
 | 
			
		||||
            return;
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Return user emails that include search terms
 | 
			
		||||
@@ -876,7 +876,8 @@ exports.filter_people_by_search_terms = function (users, search_terms) {
 | 
			
		||||
        if (match) {
 | 
			
		||||
            filtered_users.set(person.user_id, true);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return filtered_users;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -1019,15 +1020,15 @@ exports.extract_people_from_message = function (message) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Add new people involved in this message to the people list
 | 
			
		||||
    _.each(involved_people, function (person) {
 | 
			
		||||
    for (const person of involved_people) {
 | 
			
		||||
        if (person.unknown_local_echo_user) {
 | 
			
		||||
            return;
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const user_id = person.user_id || person.id;
 | 
			
		||||
 | 
			
		||||
        if (people_by_user_id_dict.has(user_id)) {
 | 
			
		||||
            return;
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        exports.report_late_add(user_id, person.email);
 | 
			
		||||
@@ -1039,7 +1040,7 @@ exports.extract_people_from_message = function (message) {
 | 
			
		||||
            is_admin: person.is_realm_admin || false,
 | 
			
		||||
            is_bot: person.is_bot || false,
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function safe_lower(s) {
 | 
			
		||||
@@ -1094,15 +1095,15 @@ exports.maybe_incr_recipient_count = function (message) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Track the number of PMs we've sent to this person to improve autocomplete
 | 
			
		||||
    _.each(message.display_recipient, function (recip) {
 | 
			
		||||
    for (const recip of message.display_recipient) {
 | 
			
		||||
 | 
			
		||||
        if (recip.unknown_local_echo_user) {
 | 
			
		||||
            return;
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const user_id = recip.id;
 | 
			
		||||
        exports.incr_recipient_count(user_id);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.set_full_name = function (person_obj, new_full_name) {
 | 
			
		||||
@@ -1181,20 +1182,20 @@ exports.is_my_user_id = function (user_id) {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.initialize = function () {
 | 
			
		||||
    _.each(page_params.realm_users, function (person) {
 | 
			
		||||
    for (const person of page_params.realm_users) {
 | 
			
		||||
        exports.add_in_realm(person);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _.each(page_params.realm_non_active_users, function (person) {
 | 
			
		||||
    for (const person of page_params.realm_non_active_users) {
 | 
			
		||||
        exports.add(person);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _.each(page_params.cross_realm_bots, function (person) {
 | 
			
		||||
    for (const person of page_params.cross_realm_bots) {
 | 
			
		||||
        if (!people_dict.has(person.email)) {
 | 
			
		||||
            exports.add(person);
 | 
			
		||||
        }
 | 
			
		||||
        cross_realm_dict.set(person.user_id, person);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    exports.initialize_current_user(page_params.user_id);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -71,9 +71,10 @@ exports.recent = (function () {
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    self.initialize = function () {
 | 
			
		||||
        _.each(page_params.recent_private_conversations, function (conversation) {
 | 
			
		||||
        for (const conversation of page_params.recent_private_conversations) {
 | 
			
		||||
            self.insert(conversation.user_ids, conversation.max_message_id);
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        delete page_params.recent_private_messages;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -53,12 +53,11 @@ exports.get_active_user_ids_string = function () {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports._get_convos = function () {
 | 
			
		||||
 | 
			
		||||
    const private_messages = pm_conversations.recent.get();
 | 
			
		||||
    const display_messages = [];
 | 
			
		||||
    const active_user_ids_string = exports.get_active_user_ids_string();
 | 
			
		||||
 | 
			
		||||
    _.each(private_messages, function (private_message_obj) {
 | 
			
		||||
    for (const private_message_obj of private_messages) {
 | 
			
		||||
        const user_ids_string = private_message_obj.user_ids_string;
 | 
			
		||||
        const reply_to = people.user_ids_string_to_emails_string(user_ids_string);
 | 
			
		||||
        const recipients_string = people.get_recipients(user_ids_string);
 | 
			
		||||
@@ -97,7 +96,8 @@ exports._get_convos = function () {
 | 
			
		||||
            is_group: is_group,
 | 
			
		||||
        };
 | 
			
		||||
        display_messages.push(display_message);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return display_messages;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -168,12 +168,12 @@ exports.poll_data_holder = function (is_my_poll, question, options) {
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // function to add all options added along with the /poll command
 | 
			
		||||
    _.each(options, function (option, i) {
 | 
			
		||||
    for (const [i, option] of options.entries()) {
 | 
			
		||||
        self.handle.new_option.inbound('canned', {
 | 
			
		||||
            idx: i,
 | 
			
		||||
            option: option,
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return self;
 | 
			
		||||
};
 | 
			
		||||
@@ -355,9 +355,10 @@ exports.activate = function (opts) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    elem.handle_events = function (events) {
 | 
			
		||||
        _.each(events, function (event) {
 | 
			
		||||
        for (const event of events) {
 | 
			
		||||
            poll_data.handle_event(event.sender_id, event.data);
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        render_question();
 | 
			
		||||
        render_results();
 | 
			
		||||
    };
 | 
			
		||||
 
 | 
			
		||||
@@ -48,7 +48,7 @@ export default function render_tabs() {
 | 
			
		||||
 | 
			
		||||
    $('#tab-total').html(total_tab_html);
 | 
			
		||||
 | 
			
		||||
    _.each(repos, function (repo) {
 | 
			
		||||
    for (const repo of repos) {
 | 
			
		||||
        // Set as the loading template for now, and load when clicked.
 | 
			
		||||
        $('#tab-' + repo).html($('#loading-template').html());
 | 
			
		||||
 | 
			
		||||
@@ -73,5 +73,5 @@ export default function render_tabs() {
 | 
			
		||||
                loaded_repos.push(repo);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -114,19 +114,19 @@ exports.update_info_for_small_realm = function () {
 | 
			
		||||
    // that the server didn't include in its presence update.
 | 
			
		||||
    const persons = people.get_realm_persons();
 | 
			
		||||
 | 
			
		||||
    _.each(persons, function (person) {
 | 
			
		||||
    for (const person of persons) {
 | 
			
		||||
        const user_id = person.user_id;
 | 
			
		||||
        let status = "offline";
 | 
			
		||||
 | 
			
		||||
        if (exports.presence_info.has(user_id)) {
 | 
			
		||||
            // this is normal, we have data for active
 | 
			
		||||
            // users that we don't want to clobber.
 | 
			
		||||
            return;
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (person.is_bot) {
 | 
			
		||||
            // we don't show presence for bots
 | 
			
		||||
            return;
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (people.is_my_user_id(user_id)) {
 | 
			
		||||
@@ -137,7 +137,7 @@ exports.update_info_for_small_realm = function () {
 | 
			
		||||
            status: status,
 | 
			
		||||
            last_active: undefined,
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.last_active_date = function (user_id) {
 | 
			
		||||
 
 | 
			
		||||
@@ -333,11 +333,11 @@ exports.remove_reaction = function (event) {
 | 
			
		||||
 | 
			
		||||
    // Do the data part first:
 | 
			
		||||
    // Remove reactions from our message object.
 | 
			
		||||
    _.each(message.reactions, function (reaction, index) {
 | 
			
		||||
    for (const [index, reaction] of message.reactions.entries()) {
 | 
			
		||||
        if (reaction.local_id === local_id && reaction.user.id === user_id) {
 | 
			
		||||
            i = index;
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (i !== -1) {
 | 
			
		||||
        message.reactions.splice(i, 1);
 | 
			
		||||
@@ -401,13 +401,14 @@ exports.get_emojis_used_by_user_for_message_id = function (message_id) {
 | 
			
		||||
 | 
			
		||||
exports.get_message_reactions = function (message) {
 | 
			
		||||
    const message_reactions = new Dict();
 | 
			
		||||
    _.each(message.reactions, function (reaction) {
 | 
			
		||||
 | 
			
		||||
    for (const reaction of message.reactions) {
 | 
			
		||||
        const user_id = reaction.user.id;
 | 
			
		||||
        reaction.local_id = exports.get_local_reaction_id(reaction);
 | 
			
		||||
        if (!people.is_known_user_id(user_id)) {
 | 
			
		||||
            blueslip.warn('Unknown user_id ' + user_id +
 | 
			
		||||
                          ' in reaction for message ' + message.id);
 | 
			
		||||
            return;
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        reaction.user_ids = [];
 | 
			
		||||
        let collapsed_reaction = message_reactions.get(reaction.local_id);
 | 
			
		||||
@@ -416,7 +417,8 @@ exports.get_message_reactions = function (message) {
 | 
			
		||||
            message_reactions.set(reaction.local_id, collapsed_reaction);
 | 
			
		||||
        }
 | 
			
		||||
        collapsed_reaction.user_ids.push(user_id);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const reactions = Array.from(message_reactions.values(), reaction => {
 | 
			
		||||
        reaction.local_id = reaction.local_id;
 | 
			
		||||
        reaction.reaction_type = reaction.reaction_type;
 | 
			
		||||
 
 | 
			
		||||
@@ -117,10 +117,11 @@ exports.initialize = function () {
 | 
			
		||||
    fragment = fragment.replace(/^reload:/, "");
 | 
			
		||||
    const keyvals = fragment.split("+");
 | 
			
		||||
    const vars = {};
 | 
			
		||||
    _.each(keyvals, function (str) {
 | 
			
		||||
 | 
			
		||||
    for (const str of keyvals) {
 | 
			
		||||
        const pair = str.split("=");
 | 
			
		||||
        vars[pair[0]] = decodeURIComponent(pair[1]);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (vars.msg !== undefined) {
 | 
			
		||||
        const send_now = parseInt(vars.send_after_reload, 10);
 | 
			
		||||
 
 | 
			
		||||
@@ -14,15 +14,16 @@ function confine_to_range(lo, val, hi) {
 | 
			
		||||
 | 
			
		||||
function size_blocks(blocks, usable_height) {
 | 
			
		||||
    let sum_height = 0;
 | 
			
		||||
    _.each(blocks, function (block) {
 | 
			
		||||
        sum_height += block.real_height;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    _.each(blocks, function (block) {
 | 
			
		||||
    for (const block of blocks) {
 | 
			
		||||
        sum_height += block.real_height;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function set_user_list_heights(res, usable_height, buddy_list_wrapper, group_pms) {
 | 
			
		||||
 
 | 
			
		||||
@@ -613,10 +613,10 @@ function make_attacher(base) {
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    self.attach_many = function (suggestions) {
 | 
			
		||||
        _.each(suggestions, function (suggestion) {
 | 
			
		||||
        for (const suggestion of suggestions) {
 | 
			
		||||
            prepend_base(suggestion);
 | 
			
		||||
            self.push(suggestion);
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return self;
 | 
			
		||||
@@ -718,12 +718,12 @@ exports.get_search_result = function (base_query, query) {
 | 
			
		||||
 | 
			
		||||
    const max_items = exports.max_num_of_search_results;
 | 
			
		||||
 | 
			
		||||
    _.each(filterers, function (filterer) {
 | 
			
		||||
    for (const filterer of filterers) {
 | 
			
		||||
        if (attacher.result.length < max_items) {
 | 
			
		||||
            const suggestions = filterer(last, base_operators);
 | 
			
		||||
            attacher.attach_many(suggestions);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return attacher.result.slice(0, max_items);
 | 
			
		||||
};
 | 
			
		||||
@@ -804,12 +804,12 @@ exports.get_search_result_legacy = function (query) {
 | 
			
		||||
 | 
			
		||||
    const max_items = exports.max_num_of_search_results;
 | 
			
		||||
 | 
			
		||||
    _.each(filterers, function (filterer) {
 | 
			
		||||
    for (const filterer of filterers) {
 | 
			
		||||
        if (attacher.result.length < max_items) {
 | 
			
		||||
            const suggestions = filterer(last, base_operators);
 | 
			
		||||
            attacher.attach_many(suggestions);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // This is unique to the legacy search system.  With pills
 | 
			
		||||
    // it is difficult to "suggest" a subset of operators,
 | 
			
		||||
@@ -835,17 +835,19 @@ exports.get_suggestions = function (base_query, query) {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.finalize_search_result = function (result) {
 | 
			
		||||
    _.each(result, function (sug) {
 | 
			
		||||
    for (const sug of result) {
 | 
			
		||||
        const first = sug.description.charAt(0).toUpperCase();
 | 
			
		||||
        sug.description = first + sug.description.slice(1);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Typeahead expects us to give it strings, not objects,
 | 
			
		||||
    // so we maintain our own hash back to our objects
 | 
			
		||||
    const lookup_table = {};
 | 
			
		||||
    _.each(result, function (obj) {
 | 
			
		||||
 | 
			
		||||
    for (const obj of result) {
 | 
			
		||||
        lookup_table[obj.search_string] = obj;
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const strings = _.map(result, function (obj) {
 | 
			
		||||
        return obj.search_string;
 | 
			
		||||
    });
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,7 @@ function get_events_success(events) {
 | 
			
		||||
        return _.pick(event, 'id', 'type', 'op');
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    _.each(events, function (event) {
 | 
			
		||||
    for (const event of events) {
 | 
			
		||||
        try {
 | 
			
		||||
            get_events_params.last_event_id = Math.max(get_events_params.last_event_id,
 | 
			
		||||
                                                       event.id);
 | 
			
		||||
@@ -29,7 +29,7 @@ function get_events_success(events) {
 | 
			
		||||
                           {event: clean_event(event)},
 | 
			
		||||
                           ex.stack);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (waiting_on_homeview_load) {
 | 
			
		||||
        events_stored_while_loading = events_stored_while_loading.concat(events);
 | 
			
		||||
@@ -77,7 +77,7 @@ function get_events_success(events) {
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    _.each(events, function (event) {
 | 
			
		||||
    for (const event of events) {
 | 
			
		||||
        try {
 | 
			
		||||
            dispatch_event(event);
 | 
			
		||||
        } catch (ex1) {
 | 
			
		||||
@@ -86,7 +86,7 @@ function get_events_success(events) {
 | 
			
		||||
                           {event: clean_event(event)},
 | 
			
		||||
                           ex1.stack);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (messages.length !== 0) {
 | 
			
		||||
        // Sort by ID, so that if we get multiple messages back from
 | 
			
		||||
@@ -98,7 +98,8 @@ function get_events_success(events) {
 | 
			
		||||
            if (messages.length > 0) {
 | 
			
		||||
                _.each(messages, message_store.set_message_booleans);
 | 
			
		||||
                let sent_by_this_client = false;
 | 
			
		||||
                _.each(messages, function (msg) {
 | 
			
		||||
 | 
			
		||||
                for (const msg of messages) {
 | 
			
		||||
                    const msg_state = sent_messages.messages[msg.local_id];
 | 
			
		||||
                    if (msg_state) {
 | 
			
		||||
                        // Almost every time, this message will be the
 | 
			
		||||
@@ -112,7 +113,8 @@ function get_events_success(events) {
 | 
			
		||||
                        // correctly.
 | 
			
		||||
                        sent_by_this_client = true;
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                message_events.insert_new_messages(messages, sent_by_this_client);
 | 
			
		||||
            }
 | 
			
		||||
        } catch (ex2) {
 | 
			
		||||
@@ -148,9 +150,9 @@ function get_events_success(events) {
 | 
			
		||||
    // We do things like updating message flags and deleting messages last,
 | 
			
		||||
    // to avoid ordering issues that are caused by batch handling of
 | 
			
		||||
    // messages above.
 | 
			
		||||
    _.each(post_message_events, function (event) {
 | 
			
		||||
    for (const event of post_message_events) {
 | 
			
		||||
        server_events_dispatch.dispatch_normal_event(event);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function show_ui_connection_error() {
 | 
			
		||||
 
 | 
			
		||||
@@ -273,13 +273,14 @@ exports.dispatch_normal_event = function dispatch_normal_event(event) {
 | 
			
		||||
            settings_streams.update_default_streams_table();
 | 
			
		||||
        } else if (event.op === 'create') {
 | 
			
		||||
            stream_data.create_streams(event.streams);
 | 
			
		||||
            _.each(event.streams, function (stream) {
 | 
			
		||||
 | 
			
		||||
            for (const stream of event.streams) {
 | 
			
		||||
                const sub = stream_data.get_sub_by_id(stream.stream_id);
 | 
			
		||||
                stream_data.update_calculated_fields(sub);
 | 
			
		||||
                subs.add_sub_to_table(sub);
 | 
			
		||||
            });
 | 
			
		||||
            }
 | 
			
		||||
        } else if (event.op === 'delete') {
 | 
			
		||||
            _.each(event.streams, function (stream) {
 | 
			
		||||
            for (const stream of event.streams) {
 | 
			
		||||
                const was_subscribed = stream_data.get_sub_by_id(stream.stream_id).subscribed;
 | 
			
		||||
                subs.remove_stream(stream.stream_id);
 | 
			
		||||
                stream_data.delete_sub(stream.stream_id);
 | 
			
		||||
@@ -298,7 +299,7 @@ exports.dispatch_normal_event = function dispatch_normal_event(event) {
 | 
			
		||||
                    settings_org.render_notifications_stream_ui(
 | 
			
		||||
                        page_params.realm_signup_notifications_stream_id, 'signup_notifications');
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
@@ -319,7 +320,7 @@ exports.dispatch_normal_event = function dispatch_normal_event(event) {
 | 
			
		||||
 | 
			
		||||
    case 'subscription':
 | 
			
		||||
        if (event.op === 'add') {
 | 
			
		||||
            _.each(event.subscriptions, function (rec) {
 | 
			
		||||
            for (const rec of event.subscriptions) {
 | 
			
		||||
                const sub = stream_data.get_sub_by_id(rec.stream_id);
 | 
			
		||||
                if (sub) {
 | 
			
		||||
                    stream_data.update_stream_email_address(sub, rec.email_address);
 | 
			
		||||
@@ -327,28 +328,28 @@ exports.dispatch_normal_event = function dispatch_normal_event(event) {
 | 
			
		||||
                } else {
 | 
			
		||||
                    blueslip.error('Subscribing to unknown stream with ID ' + rec.stream_id);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            }
 | 
			
		||||
        } else if (event.op === 'peer_add') {
 | 
			
		||||
            _.each(event.subscriptions, function (sub) {
 | 
			
		||||
            for (const sub of event.subscriptions) {
 | 
			
		||||
                if (stream_data.add_subscriber(sub, event.user_id)) {
 | 
			
		||||
                    $(document).trigger('peer_subscribe.zulip', {stream_name: sub});
 | 
			
		||||
                } else {
 | 
			
		||||
                    blueslip.warn('Cannot process peer_add event');
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            }
 | 
			
		||||
        } else if (event.op === 'peer_remove') {
 | 
			
		||||
            _.each(event.subscriptions, function (sub) {
 | 
			
		||||
            for (const sub of event.subscriptions) {
 | 
			
		||||
                if (stream_data.remove_subscriber(sub, event.user_id)) {
 | 
			
		||||
                    $(document).trigger('peer_unsubscribe.zulip', {stream_name: sub});
 | 
			
		||||
                } else {
 | 
			
		||||
                    blueslip.warn('Cannot process peer_remove event.');
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            }
 | 
			
		||||
        } else if (event.op === 'remove') {
 | 
			
		||||
            _.each(event.subscriptions, function (rec) {
 | 
			
		||||
            for (const rec of event.subscriptions) {
 | 
			
		||||
                const sub = stream_data.get_sub_by_id(rec.stream_id);
 | 
			
		||||
                stream_events.mark_unsubscribed(sub);
 | 
			
		||||
            });
 | 
			
		||||
            }
 | 
			
		||||
        } else if (event.op === 'update') {
 | 
			
		||||
            stream_events.update_property(
 | 
			
		||||
                event.stream_id,
 | 
			
		||||
@@ -469,9 +470,10 @@ exports.dispatch_normal_event = function dispatch_normal_event(event) {
 | 
			
		||||
        const new_value = event.operation === "add";
 | 
			
		||||
        switch (event.flag) {
 | 
			
		||||
        case 'starred':
 | 
			
		||||
            _.each(event.messages, function (message_id) {
 | 
			
		||||
            for (const message_id of event.messages) {
 | 
			
		||||
                message_flags.update_starred_flag(message_id, new_value);
 | 
			
		||||
            });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (event.operation === "add") {
 | 
			
		||||
                starred_messages.add(event.messages);
 | 
			
		||||
            } else {
 | 
			
		||||
 
 | 
			
		||||
@@ -100,9 +100,10 @@ function update_user_custom_profile_fields(fields, method) {
 | 
			
		||||
    if (method === undefined) {
 | 
			
		||||
        blueslip.error("Undefined method in update_user_custom_profile_fields");
 | 
			
		||||
    }
 | 
			
		||||
    _.each(fields, function (field) {
 | 
			
		||||
 | 
			
		||||
    for (const field of fields) {
 | 
			
		||||
        update_custom_profile_field(field, method);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
exports.append_custom_profile_fields = function (element_id, user_id) {
 | 
			
		||||
 
 | 
			
		||||
@@ -77,7 +77,7 @@ exports.render_bots = function () {
 | 
			
		||||
    const all_bots_for_current_user = bot_data.get_all_bots_for_current_user();
 | 
			
		||||
    let user_owns_an_active_bot = false;
 | 
			
		||||
 | 
			
		||||
    _.each(all_bots_for_current_user, function (elem) {
 | 
			
		||||
    for (const elem of all_bots_for_current_user) {
 | 
			
		||||
        add_bot_row({
 | 
			
		||||
            name: elem.full_name,
 | 
			
		||||
            email: elem.email,
 | 
			
		||||
@@ -89,7 +89,7 @@ exports.render_bots = function () {
 | 
			
		||||
            zuliprc: 'zuliprc', // Most browsers do not allow filename starting with `.`
 | 
			
		||||
        });
 | 
			
		||||
        user_owns_an_active_bot = user_owns_an_active_bot || elem.is_active;
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (exports.can_create_new_bots()) {
 | 
			
		||||
        if (!user_owns_an_active_bot) {
 | 
			
		||||
@@ -217,12 +217,14 @@ exports.set_up = function () {
 | 
			
		||||
    $('#download_botserverrc').click(function () {
 | 
			
		||||
        const OUTGOING_WEBHOOK_BOT_TYPE_INT = 3;
 | 
			
		||||
        let content = "";
 | 
			
		||||
        _.each(bot_data.get_all_bots_for_current_user(), function (bot) {
 | 
			
		||||
 | 
			
		||||
        for (const bot of bot_data.get_all_bots_for_current_user()) {
 | 
			
		||||
            if (bot.is_active && bot.bot_type === OUTGOING_WEBHOOK_BOT_TYPE_INT) {
 | 
			
		||||
                const bot_token = bot_data.get_services(bot.user_id)[0].token;
 | 
			
		||||
                content += exports.generate_botserverrc_content(bot.email, bot.api_key, bot_token);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $(this).attr("href", "data:application/octet-stream;charset=utf-8," + encodeURIComponent(content));
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -77,7 +77,7 @@ exports.set_up = function () {
 | 
			
		||||
        overlays.close_modal('default_language_modal');
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    _.each(exports.all_display_settings.settings.user_display_settings, function (setting) {
 | 
			
		||||
    for (const setting of exports.all_display_settings.settings.user_display_settings) {
 | 
			
		||||
        $("#" + setting).change(function () {
 | 
			
		||||
            const data = {};
 | 
			
		||||
            data[setting] = JSON.stringify($(this).prop('checked'));
 | 
			
		||||
@@ -91,7 +91,7 @@ exports.set_up = function () {
 | 
			
		||||
                change_display_setting(data, "#display-settings-status");
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $("#default_language_modal .language").click(function (e) {
 | 
			
		||||
        e.preventDefault();
 | 
			
		||||
 
 | 
			
		||||
@@ -134,9 +134,11 @@ exports.set_up = function () {
 | 
			
		||||
        $('#admin_emoji_submit').attr('disabled', true);
 | 
			
		||||
        const emoji = {};
 | 
			
		||||
        const formData = new FormData();
 | 
			
		||||
        _.each($(this).serializeArray(), function (obj) {
 | 
			
		||||
 | 
			
		||||
        for (const obj of $(this).serializeArray()) {
 | 
			
		||||
            emoji[obj.name] = obj.value;
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $.each($('#emoji_file_input')[0].files, function (i, file) {
 | 
			
		||||
            formData.append('file-' + i, file);
 | 
			
		||||
        });
 | 
			
		||||
 
 | 
			
		||||
@@ -121,9 +121,10 @@ exports.build_page = function () {
 | 
			
		||||
        pattern_status.hide();
 | 
			
		||||
        format_status.hide();
 | 
			
		||||
        const filter = {};
 | 
			
		||||
        _.each($(this).serializeArray(), function (obj) {
 | 
			
		||||
 | 
			
		||||
        for (const obj of $(this).serializeArray()) {
 | 
			
		||||
            filter[obj.name] = obj.value;
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        channel.post({
 | 
			
		||||
            url: "/json/realm/filters",
 | 
			
		||||
 
 | 
			
		||||
@@ -97,8 +97,8 @@ exports.set_enable_digest_emails_visibility = function () {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.set_up = function () {
 | 
			
		||||
    _.each(notification_settings_status, function (setting) {
 | 
			
		||||
        _.each(setting.settings, function (sub_setting) {
 | 
			
		||||
    for (const setting of notification_settings_status) {
 | 
			
		||||
        for (const sub_setting of setting.settings) {
 | 
			
		||||
            $("#" + sub_setting).change(function () {
 | 
			
		||||
                let value;
 | 
			
		||||
 | 
			
		||||
@@ -113,8 +113,8 @@ exports.set_up = function () {
 | 
			
		||||
                change_notification_setting(sub_setting, value,
 | 
			
		||||
                                            "#" + setting.status_label);
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    update_desktop_icon_count_display();
 | 
			
		||||
 | 
			
		||||
@@ -138,19 +138,19 @@ exports.set_up = function () {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.update_page = function () {
 | 
			
		||||
    _.each(exports.all_notification_settings_labels, function (setting) {
 | 
			
		||||
    for (const setting of exports.all_notification_settings_labels) {
 | 
			
		||||
        if (setting === 'enable_offline_push_notifications'
 | 
			
		||||
            && !page_params.realm_push_notifications_enabled) {
 | 
			
		||||
            // If push notifications are disabled at the realm level,
 | 
			
		||||
            // we should just leave the checkbox always off.
 | 
			
		||||
            return;
 | 
			
		||||
            continue;
 | 
			
		||||
        } else if (setting === 'desktop_icon_count_display') {
 | 
			
		||||
            update_desktop_icon_count_display();
 | 
			
		||||
            return;
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $("#" + setting).prop('checked', page_params[setting]);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
window.settings_notifications = exports;
 | 
			
		||||
 
 | 
			
		||||
@@ -384,20 +384,23 @@ exports.populate_realm_domains = function (realm_domains) {
 | 
			
		||||
 | 
			
		||||
    const realm_domains_table_body = $("#realm_domains_table tbody").expectOne();
 | 
			
		||||
    realm_domains_table_body.find("tr").remove();
 | 
			
		||||
    _.each(realm_domains, function (realm_domain) {
 | 
			
		||||
 | 
			
		||||
    for (const realm_domain of realm_domains) {
 | 
			
		||||
        realm_domains_table_body.append(
 | 
			
		||||
            render_settings_admin_realm_domains_list({
 | 
			
		||||
                realm_domain: realm_domain,
 | 
			
		||||
            })
 | 
			
		||||
        );
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
function sort_object_by_key(obj) {
 | 
			
		||||
    const keys = _.keys(obj).sort();
 | 
			
		||||
    const new_obj = {};
 | 
			
		||||
    _.each(keys, function (key) {
 | 
			
		||||
 | 
			
		||||
    for (const key of keys) {
 | 
			
		||||
        new_obj[key] = obj[key];
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return new_obj;
 | 
			
		||||
}
 | 
			
		||||
exports.populate_auth_methods = function (auth_methods) {
 | 
			
		||||
@@ -672,9 +675,11 @@ exports.build_page = function () {
 | 
			
		||||
    function get_auth_method_table_data() {
 | 
			
		||||
        const new_auth_methods = {};
 | 
			
		||||
        const auth_method_rows = $("#id_realm_authentication_methods").find('tr.method_row');
 | 
			
		||||
        _.each(auth_method_rows, function (method_row) {
 | 
			
		||||
 | 
			
		||||
        for (const method_row of auth_method_rows) {
 | 
			
		||||
            new_auth_methods[$(method_row).data('method')] = $(method_row).find('input').prop('checked');
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return new_auth_methods;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -711,11 +716,12 @@ exports.build_page = function () {
 | 
			
		||||
        subsection.find('.save-button').show();
 | 
			
		||||
        const properties_elements = get_subsection_property_elements(subsection);
 | 
			
		||||
        let show_change_process_button = false;
 | 
			
		||||
        _.each(properties_elements, function (elem) {
 | 
			
		||||
 | 
			
		||||
        for (const elem of properties_elements) {
 | 
			
		||||
            if (check_property_changed(elem)) {
 | 
			
		||||
                show_change_process_button = true;
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const save_btn_controls = subsection.find('.subsection-header .save-button-controls');
 | 
			
		||||
        const button_state = show_change_process_button ? "unsaved" : "discarded";
 | 
			
		||||
@@ -872,7 +878,8 @@ exports.build_page = function () {
 | 
			
		||||
    function populate_data_for_request(subsection) {
 | 
			
		||||
        const data = {};
 | 
			
		||||
        const properties_elements = get_subsection_property_elements(subsection);
 | 
			
		||||
        _.each(properties_elements, function (input_elem) {
 | 
			
		||||
 | 
			
		||||
        for (let input_elem of properties_elements) {
 | 
			
		||||
            input_elem = $(input_elem);
 | 
			
		||||
            if (check_property_changed(input_elem)) {
 | 
			
		||||
                const input_type = input_elem.data("setting-widget-type");
 | 
			
		||||
@@ -880,18 +887,19 @@ exports.build_page = function () {
 | 
			
		||||
                    const property_name = input_elem.attr('id').replace("id_realm_", "");
 | 
			
		||||
                    if (input_type === 'bool') {
 | 
			
		||||
                        data[property_name] = JSON.stringify(input_elem.prop('checked'));
 | 
			
		||||
                        return;
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
                    if (input_type === 'text') {
 | 
			
		||||
                        data[property_name] = JSON.stringify(input_elem.val().trim());
 | 
			
		||||
                        return;
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
                    if (input_type === 'integer') {
 | 
			
		||||
                        data[property_name] = JSON.stringify(parseInt(input_elem.val().trim(), 10));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -247,13 +247,13 @@ function set_up_choices_field_edit_form(profile_field, field_data) {
 | 
			
		||||
 | 
			
		||||
    const choices_data = exports.parse_field_choices_from_field_data(field_data);
 | 
			
		||||
 | 
			
		||||
    _.each(choices_data, function (choice) {
 | 
			
		||||
    for (const choice of choices_data) {
 | 
			
		||||
        choice_list.append(
 | 
			
		||||
            render_settings_profile_field_choice({
 | 
			
		||||
                text: choice.text,
 | 
			
		||||
            })
 | 
			
		||||
        );
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Add blank choice at last
 | 
			
		||||
    create_choice_row(choice_list);
 | 
			
		||||
@@ -350,7 +350,8 @@ exports.do_populate_profile_fields = function (profile_fields_data) {
 | 
			
		||||
    profile_fields_table.find("tr.profile-field-row").remove();  // Clear all rows.
 | 
			
		||||
    profile_fields_table.find("tr.profile-field-form").remove();  // Clear all rows.
 | 
			
		||||
    order = [];
 | 
			
		||||
    _.each(profile_fields_data, function (profile_field) {
 | 
			
		||||
 | 
			
		||||
    for (const profile_field of profile_fields_data) {
 | 
			
		||||
        order.push(profile_field.id);
 | 
			
		||||
        let field_data = {};
 | 
			
		||||
        if (profile_field.field_data) {
 | 
			
		||||
@@ -377,7 +378,8 @@ exports.do_populate_profile_fields = function (profile_fields_data) {
 | 
			
		||||
                realm_default_external_accounts: page_params.realm_default_external_accounts,
 | 
			
		||||
            })
 | 
			
		||||
        );
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (page_params.is_admin) {
 | 
			
		||||
        const field_list = $("#admin_profile_fields_table")[0];
 | 
			
		||||
        Sortable.create(field_list, {
 | 
			
		||||
 
 | 
			
		||||
@@ -34,19 +34,18 @@ exports.toggle_org_setting_collapse = function () {
 | 
			
		||||
    const show_more_settings_text = i18n.t("Show more");
 | 
			
		||||
 | 
			
		||||
    if (is_collapsed) {
 | 
			
		||||
        _.each($(".collapse-org-settings"), function (elem) {
 | 
			
		||||
        for (const elem of $(".collapse-org-settings")) {
 | 
			
		||||
            $(elem).removeClass("hide-org-settings");
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $("#toggle_collapse_chevron").removeClass("fa-angle-double-down");
 | 
			
		||||
        $("#toggle_collapse_chevron").addClass("fa-angle-double-up");
 | 
			
		||||
 | 
			
		||||
        $("#toggle_collapse").text(show_fewer_settings_text);
 | 
			
		||||
 | 
			
		||||
    } else {
 | 
			
		||||
        _.each($(".collapse-org-settings"), function (elem) {
 | 
			
		||||
        for (const elem of $(".collapse-org-settings")) {
 | 
			
		||||
            $(elem).addClass("hide-org-settings");
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $("#toggle_collapse_chevron").removeClass("fa-angle-double-up");
 | 
			
		||||
        $("#toggle_collapse_chevron").addClass("fa-angle-double-down");
 | 
			
		||||
 
 | 
			
		||||
@@ -38,10 +38,10 @@ exports.can_edit = function (group_id) {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.populate_user_groups = function () {
 | 
			
		||||
 | 
			
		||||
    const user_groups_section = $('#user-groups').expectOne();
 | 
			
		||||
    const user_groups_array = user_groups.get_realm_user_groups();
 | 
			
		||||
    _.each(user_groups_array, function (data) {
 | 
			
		||||
 | 
			
		||||
    for (const data of user_groups_array) {
 | 
			
		||||
        user_groups_section.append(render_admin_user_group_list({
 | 
			
		||||
            user_group: {
 | 
			
		||||
                name: data.name,
 | 
			
		||||
@@ -257,7 +257,7 @@ exports.populate_user_groups = function () {
 | 
			
		||||
                }, 100);
 | 
			
		||||
            });
 | 
			
		||||
        }());
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.set_up = function () {
 | 
			
		||||
@@ -273,12 +273,13 @@ exports.set_up = function () {
 | 
			
		||||
        const group = {
 | 
			
		||||
            members: JSON.stringify([people.my_current_user_id()]),
 | 
			
		||||
        };
 | 
			
		||||
        _.each($(this).serializeArray(), function (obj) {
 | 
			
		||||
 | 
			
		||||
        for (const obj of $(this).serializeArray()) {
 | 
			
		||||
            if (obj.value.trim() === "") {
 | 
			
		||||
                return;
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            group[obj.name] = obj.value;
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        channel.post({
 | 
			
		||||
            url: "/json/user_groups/create",
 | 
			
		||||
 
 | 
			
		||||
@@ -2,23 +2,27 @@ exports.ids = new Set();
 | 
			
		||||
 | 
			
		||||
exports.initialize = function () {
 | 
			
		||||
    exports.ids.clear();
 | 
			
		||||
    _.each(page_params.starred_messages, function (id) {
 | 
			
		||||
 | 
			
		||||
    for (const id of page_params.starred_messages) {
 | 
			
		||||
        exports.ids.add(id);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    exports.rerender_ui();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.add = function (ids) {
 | 
			
		||||
    _.each(ids, function (id) {
 | 
			
		||||
    for (const id of ids) {
 | 
			
		||||
        exports.ids.add(id);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    exports.rerender_ui();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.remove = function (ids) {
 | 
			
		||||
    _.each(ids, function (id) {
 | 
			
		||||
    for (const id of ids) {
 | 
			
		||||
        exports.ids.delete(id);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    exports.rerender_ui();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@ function update_table_stream_color(table, stream_name, color) {
 | 
			
		||||
 | 
			
		||||
    const stream_labels = $("#floating_recipient_bar").add(table).find(".stream_label");
 | 
			
		||||
 | 
			
		||||
    _.each(stream_labels, function (label) {
 | 
			
		||||
    for (const label of stream_labels) {
 | 
			
		||||
        const $label = $(label);
 | 
			
		||||
        if ($.trim($label.text()) === stream_name) {
 | 
			
		||||
            const messages = $label.closest(".recipient_row").children(".message_row");
 | 
			
		||||
@@ -21,7 +21,7 @@ function update_table_stream_color(table, stream_name, color) {
 | 
			
		||||
            $label.removeClass(exports.color_classes);
 | 
			
		||||
            $label.addClass(color_class);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function update_stream_sidebar_swatch_color(id, color) {
 | 
			
		||||
 
 | 
			
		||||
@@ -305,9 +305,9 @@ exports.get_updated_unsorted_subs = function () {
 | 
			
		||||
    let all_subs = Array.from(stream_info.values());
 | 
			
		||||
 | 
			
		||||
    // Add in admin options and stream counts.
 | 
			
		||||
    _.each(all_subs, function (sub) {
 | 
			
		||||
    for (const sub of all_subs) {
 | 
			
		||||
        exports.update_calculated_fields(sub);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // We don't display unsubscribed streams to guest users.
 | 
			
		||||
    if (page_params.is_guest) {
 | 
			
		||||
@@ -447,9 +447,9 @@ exports.update_calculated_fields = function (sub) {
 | 
			
		||||
    exports.update_subscribers_count(sub);
 | 
			
		||||
 | 
			
		||||
    // Apply the defaults for our notification settings for rendering.
 | 
			
		||||
    _.each(stream_notification_settings, function (setting) {
 | 
			
		||||
    for (const setting of stream_notification_settings) {
 | 
			
		||||
        sub[setting + "_display"] = exports.receives_notifications(sub.name, setting);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.all_subscribed_streams_are_in_home_view = function () {
 | 
			
		||||
@@ -672,14 +672,14 @@ exports.is_user_subscribed = function (stream_name, user_id) {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.create_streams = function (streams) {
 | 
			
		||||
    _.each(streams, function (stream) {
 | 
			
		||||
    for (const stream of streams) {
 | 
			
		||||
        // We handle subscriber stuff in other events.
 | 
			
		||||
        const attrs = _.defaults(stream, {
 | 
			
		||||
            subscribers: [],
 | 
			
		||||
            subscribed: false,
 | 
			
		||||
        });
 | 
			
		||||
        exports.create_sub_from_server_data(stream.name, attrs);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.create_sub_from_server_data = function (stream_name, attrs) {
 | 
			
		||||
@@ -754,9 +754,9 @@ exports.get_streams_for_settings_page = function () {
 | 
			
		||||
    const all_subs = unsubscribed_rows.concat(subscribed_rows);
 | 
			
		||||
 | 
			
		||||
    // Add in admin options and stream counts.
 | 
			
		||||
    _.each(all_subs, function (sub) {
 | 
			
		||||
    for (const sub of all_subs) {
 | 
			
		||||
        exports.update_calculated_fields(sub);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return all_subs;
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -133,12 +133,12 @@ exports.mark_unsubscribed = function (sub) {
 | 
			
		||||
exports.remove_deactivated_user_from_all_streams = function (user_id) {
 | 
			
		||||
    const all_subs = stream_data.get_unsorted_subs();
 | 
			
		||||
 | 
			
		||||
    _.each(all_subs, function (sub) {
 | 
			
		||||
    for (const sub of all_subs) {
 | 
			
		||||
        if (stream_data.is_user_subscribed(sub.name, user_id)) {
 | 
			
		||||
            stream_data.remove_subscriber(sub.name, user_id);
 | 
			
		||||
            subs.rerender_subscriptions_settings(sub);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -74,9 +74,9 @@ exports.create_initial_sidebar_rows = function () {
 | 
			
		||||
    // structures that are kept in stream_data.js.
 | 
			
		||||
    const subs = stream_data.subscribed_subs();
 | 
			
		||||
 | 
			
		||||
    _.each(subs, function (sub) {
 | 
			
		||||
    for (const sub of subs) {
 | 
			
		||||
        exports.create_sidebar_row(sub);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.build_stream_list = function () {
 | 
			
		||||
 
 | 
			
		||||
@@ -48,7 +48,7 @@ exports.sort_groups = function (streams, search_term) {
 | 
			
		||||
    const normal_streams = [];
 | 
			
		||||
    const dormant_streams = [];
 | 
			
		||||
 | 
			
		||||
    _.each(streams, function (stream) {
 | 
			
		||||
    for (const stream of streams) {
 | 
			
		||||
        const sub = stream_data.get_sub(stream);
 | 
			
		||||
        const pinned = sub.pin_to_top;
 | 
			
		||||
        if (pinned) {
 | 
			
		||||
@@ -58,7 +58,7 @@ exports.sort_groups = function (streams, search_term) {
 | 
			
		||||
        } else {
 | 
			
		||||
            dormant_streams.push(stream);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pinned_streams.sort(util.strcmp);
 | 
			
		||||
    normal_streams.sort(util.strcmp);
 | 
			
		||||
 
 | 
			
		||||
@@ -290,11 +290,11 @@ exports.show_active_stream_in_left_panel = function () {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.add_tooltips_to_left_panel = function () {
 | 
			
		||||
    _.each($("#subscriptions_table .stream-row"), function (row) {
 | 
			
		||||
    for (const row of $("#subscriptions_table .stream-row")) {
 | 
			
		||||
        $(row).find('.sub-info-box [class$="-bar"] [class$="-count"]').tooltip({
 | 
			
		||||
            placement: 'left', animation: false,
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.update_settings_for_unsubscribed = function (sub) {
 | 
			
		||||
@@ -358,7 +358,7 @@ function get_stream_id_buckets(stream_ids, query) {
 | 
			
		||||
        other: [],
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    _.each(stream_ids, function (stream_id) {
 | 
			
		||||
    for (const stream_id of stream_ids) {
 | 
			
		||||
        const sub = stream_data.get_sub_by_id(stream_id);
 | 
			
		||||
        const match_status = triage_stream(query, sub);
 | 
			
		||||
 | 
			
		||||
@@ -369,7 +369,7 @@ function get_stream_id_buckets(stream_ids, query) {
 | 
			
		||||
        } else {
 | 
			
		||||
            buckets.other.push(stream_id);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    stream_data.sort_for_stream_settings(buckets.name);
 | 
			
		||||
    stream_data.sort_for_stream_settings(buckets.desc);
 | 
			
		||||
@@ -404,21 +404,23 @@ exports.filter_table = function (query) {
 | 
			
		||||
    const streams_list_scrolltop = ui.get_scroll_element($(".streams-list")).scrollTop();
 | 
			
		||||
 | 
			
		||||
    const stream_ids = [];
 | 
			
		||||
    _.each($("#subscriptions_table .stream-row"), function (row) {
 | 
			
		||||
 | 
			
		||||
    for (const row of $("#subscriptions_table .stream-row")) {
 | 
			
		||||
        const stream_id = stream_id_for_row(row);
 | 
			
		||||
        stream_ids.push(stream_id);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const buckets = get_stream_id_buckets(stream_ids, query);
 | 
			
		||||
 | 
			
		||||
    // If we just re-built the DOM from scratch we wouldn't need
 | 
			
		||||
    // all this hidden/notdisplayed logic.
 | 
			
		||||
    const hidden_ids = {};
 | 
			
		||||
    _.each(buckets.other, function (stream_id) {
 | 
			
		||||
        hidden_ids[stream_id] = true;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    _.each($("#subscriptions_table .stream-row"), function (row) {
 | 
			
		||||
    for (const stream_id of buckets.other) {
 | 
			
		||||
        hidden_ids[stream_id] = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (const row of $("#subscriptions_table .stream-row")) {
 | 
			
		||||
        const stream_id = stream_id_for_row(row);
 | 
			
		||||
 | 
			
		||||
        // Below code goes away if we don't do sort-DOM-in-place.
 | 
			
		||||
@@ -429,7 +431,7 @@ exports.filter_table = function (query) {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        widgets[stream_id] = $(row).detach();
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    exports.add_tooltips_to_left_panel();
 | 
			
		||||
 | 
			
		||||
@@ -441,9 +443,9 @@ exports.filter_table = function (query) {
 | 
			
		||||
        buckets.other
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    _.each(all_stream_ids, function (stream_id) {
 | 
			
		||||
    for (const stream_id of all_stream_ids) {
 | 
			
		||||
        ui.get_content_element($('#subscriptions_table .streams-list')).append(widgets[stream_id]);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    exports.maybe_reset_right_panel();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -138,9 +138,10 @@ exports.activate = function (opts) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    elem.handle_events = function (events) {
 | 
			
		||||
        _.each(events, function (event) {
 | 
			
		||||
        for (const event of events) {
 | 
			
		||||
            tictactoe_data.handle_event(event.sender_id, event.data);
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        render();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -172,14 +172,14 @@ exports.update_timestamps = function () {
 | 
			
		||||
        const to_process = update_list;
 | 
			
		||||
        update_list = [];
 | 
			
		||||
 | 
			
		||||
        _.each(to_process, function (entry) {
 | 
			
		||||
        for (const entry of to_process) {
 | 
			
		||||
            const className = entry.className;
 | 
			
		||||
            const elements = $('.' + className);
 | 
			
		||||
            // The element might not exist any more (because it
 | 
			
		||||
            // was in the zfilt table, or because we added
 | 
			
		||||
            // messages above it and re-collapsed).
 | 
			
		||||
            if (elements !== null) {
 | 
			
		||||
                _.each(elements, function (element) {
 | 
			
		||||
                for (const element of elements) {
 | 
			
		||||
                    const time = entry.time;
 | 
			
		||||
                    const time_above = entry.time_above;
 | 
			
		||||
                    const rendered_time = exports.render_now(time);
 | 
			
		||||
@@ -195,9 +195,9 @@ exports.update_timestamps = function () {
 | 
			
		||||
                        time: time,
 | 
			
		||||
                        time_above: time_above,
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
                }
 | 
			
		||||
        });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        next_update = set_to_start_of_day(now.clone().addDays(1));
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -162,9 +162,10 @@ exports.activate = function (opts) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    elem.handle_events = function (events) {
 | 
			
		||||
        _.each(events, function (event) {
 | 
			
		||||
        for (const event of events) {
 | 
			
		||||
            task_data.handle_event(event.sender_id, event.data);
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        render_results();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -114,7 +114,7 @@ exports.topic_history = function (stream_id) {
 | 
			
		||||
        // server.  We have less data about these than the
 | 
			
		||||
        // client can maintain for newer topics.
 | 
			
		||||
 | 
			
		||||
        _.each(server_history, function (obj) {
 | 
			
		||||
        for (const obj of server_history) {
 | 
			
		||||
            const name = obj.name;
 | 
			
		||||
            const message_id = obj.max_id;
 | 
			
		||||
 | 
			
		||||
@@ -124,7 +124,7 @@ exports.topic_history = function (stream_id) {
 | 
			
		||||
                if (!existing.historical) {
 | 
			
		||||
                    // Trust out local data more, since it
 | 
			
		||||
                    // maintains counts.
 | 
			
		||||
                    return;
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@@ -137,7 +137,7 @@ exports.topic_history = function (stream_id) {
 | 
			
		||||
                pretty_name: name,
 | 
			
		||||
                historical: true,
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    self.get_recent_names = function () {
 | 
			
		||||
 
 | 
			
		||||
@@ -15,13 +15,13 @@ exports.get_list_info = function (stream_id, zoomed) {
 | 
			
		||||
 | 
			
		||||
    const items = [];
 | 
			
		||||
 | 
			
		||||
    _.each(topic_names, function (topic_name, idx) {
 | 
			
		||||
    for (const [idx, topic_name] of topic_names.entries()) {
 | 
			
		||||
        const num_unread = unread.num_unread_for_topic(stream_id, topic_name);
 | 
			
		||||
        const is_active_topic = active_topic === topic_name.toLowerCase();
 | 
			
		||||
        const is_topic_muted = muting.is_topic_muted(stream_id, topic_name);
 | 
			
		||||
 | 
			
		||||
        if (!zoomed) {
 | 
			
		||||
            function should_show_topic() {
 | 
			
		||||
            function should_show_topic(topics_selected) {
 | 
			
		||||
                // This function exists just for readability, to
 | 
			
		||||
                // avoid long chained conditionals to determine
 | 
			
		||||
                // which topics to include.
 | 
			
		||||
@@ -62,7 +62,7 @@ exports.get_list_info = function (stream_id, zoomed) {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const show_topic = should_show_topic();
 | 
			
		||||
            const show_topic = should_show_topic(topics_selected);
 | 
			
		||||
            if (!show_topic) {
 | 
			
		||||
                if (!is_topic_muted) {
 | 
			
		||||
                    // The "more topics" unread count, like
 | 
			
		||||
@@ -70,7 +70,7 @@ exports.get_list_info = function (stream_id, zoomed) {
 | 
			
		||||
                    // on unmuted topics.
 | 
			
		||||
                    more_topics_unreads += num_unread;
 | 
			
		||||
                }
 | 
			
		||||
                return;
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            topics_selected += 1;
 | 
			
		||||
            // We fall through to rendering the topic, using the
 | 
			
		||||
@@ -87,7 +87,7 @@ exports.get_list_info = function (stream_id, zoomed) {
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        items.push(topic_info);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        items: items,
 | 
			
		||||
 
 | 
			
		||||
@@ -27,13 +27,15 @@ exports.highlight_with_escaping_and_regex = function (regex, item) {
 | 
			
		||||
 | 
			
		||||
    const pieces = item.split(regex);
 | 
			
		||||
    let result = "";
 | 
			
		||||
    _.each(pieces, function (piece) {
 | 
			
		||||
 | 
			
		||||
    for (const piece of pieces) {
 | 
			
		||||
        if (piece.match(regex)) {
 | 
			
		||||
            result += "<strong>" + Handlebars.Utils.escapeExpression(piece) + "</strong>";
 | 
			
		||||
        } else {
 | 
			
		||||
            result += Handlebars.Utils.escapeExpression(piece);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return result;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -304,7 +306,6 @@ exports.sort_recipients = function (
 | 
			
		||||
    groups,
 | 
			
		||||
    max_num_items
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
    if (!groups) {
 | 
			
		||||
        groups = [];
 | 
			
		||||
    }
 | 
			
		||||
@@ -356,11 +357,12 @@ exports.sort_recipients = function (
 | 
			
		||||
    */
 | 
			
		||||
 | 
			
		||||
    let items = [];
 | 
			
		||||
    _.each(getters, (getter) => {
 | 
			
		||||
 | 
			
		||||
    for (const getter of getters) {
 | 
			
		||||
        if (items.length < max_num_items) {
 | 
			
		||||
            items = items.concat(getter());
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return items.slice(0, max_num_items);
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -44,18 +44,18 @@ exports.reset_scrollbar = function (element_selector) {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function update_message_in_all_views(message_id, callback) {
 | 
			
		||||
    _.each([message_list.all, home_msg_list, message_list.narrowed], function (list) {
 | 
			
		||||
    for (const list of [message_list.all, home_msg_list, message_list.narrowed]) {
 | 
			
		||||
        if (list === undefined) {
 | 
			
		||||
            return;
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        const row = list.get_row(message_id);
 | 
			
		||||
        if (row === undefined) {
 | 
			
		||||
            // The row may not exist, e.g. if you do an action on a message in
 | 
			
		||||
            // a narrowed view
 | 
			
		||||
            return;
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        callback(row);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
exports.show_error_for_unsupported_platform = function () {
 | 
			
		||||
@@ -78,11 +78,13 @@ exports.find_message = function (message_id) {
 | 
			
		||||
    // (if it was loaded when narrowed), or only in the message_list.all
 | 
			
		||||
    // (if received from the server while in a different narrow)
 | 
			
		||||
    let message;
 | 
			
		||||
    _.each([message_list.all, home_msg_list, message_list.narrowed], function (msg_list) {
 | 
			
		||||
 | 
			
		||||
    for (const msg_list of [message_list.all, home_msg_list, message_list.narrowed]) {
 | 
			
		||||
        if (msg_list !== undefined && message === undefined) {
 | 
			
		||||
            message = msg_list.get(message_id);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return message;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -113,15 +115,15 @@ exports.show_message_failed = function (message_id, failed_msg) {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.remove_message = function (message_id) {
 | 
			
		||||
    _.each([message_list.all, home_msg_list, message_list.narrowed], function (list) {
 | 
			
		||||
    for (const list of [message_list.all, home_msg_list, message_list.narrowed]) {
 | 
			
		||||
        if (list === undefined) {
 | 
			
		||||
            return;
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        const row = list.get_row(message_id);
 | 
			
		||||
        if (row !== undefined) {
 | 
			
		||||
            list.remove_and_rerender([{id: message_id}]);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.show_failed_message_success = function (message_id) {
 | 
			
		||||
 
 | 
			
		||||
@@ -93,26 +93,26 @@ exports.unread_pm_counter = (function () {
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    self.set_pms = function (pms) {
 | 
			
		||||
        _.each(pms, function (obj) {
 | 
			
		||||
        for (const obj of pms) {
 | 
			
		||||
            const user_ids_string = obj.sender_id.toString();
 | 
			
		||||
            self.set_message_ids(user_ids_string, obj.unread_message_ids);
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    self.set_huddles = function (huddles) {
 | 
			
		||||
        _.each(huddles, function (obj) {
 | 
			
		||||
        for (const obj of huddles) {
 | 
			
		||||
            const user_ids_string = people.pm_lookup_key(obj.user_ids_string);
 | 
			
		||||
            self.set_message_ids(user_ids_string, obj.unread_message_ids);
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    self.set_message_ids = function (user_ids_string, unread_message_ids) {
 | 
			
		||||
        _.each(unread_message_ids, function (msg_id) {
 | 
			
		||||
        for (const msg_id of unread_message_ids) {
 | 
			
		||||
            bucketer.add({
 | 
			
		||||
                bucket_key: user_ids_string,
 | 
			
		||||
                item_id: msg_id,
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    self.add = function (message) {
 | 
			
		||||
@@ -208,15 +208,15 @@ exports.unread_topic_counter = (function () {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    self.set_streams = function (objs) {
 | 
			
		||||
        _.each(objs, function (obj) {
 | 
			
		||||
        for (const obj of objs) {
 | 
			
		||||
            const stream_id = obj.stream_id;
 | 
			
		||||
            const topic = obj.topic;
 | 
			
		||||
            const unread_message_ids = obj.unread_message_ids;
 | 
			
		||||
 | 
			
		||||
            _.each(unread_message_ids, function (msg_id) {
 | 
			
		||||
            for (const msg_id of unread_message_ids) {
 | 
			
		||||
                self.add(stream_id, topic, msg_id);
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    self.add = function (stream_id, topic, msg_id) {
 | 
			
		||||
@@ -424,9 +424,9 @@ exports.update_unread_topics = function (msg, event) {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.process_loaded_messages = function (messages) {
 | 
			
		||||
    _.each(messages, function (message) {
 | 
			
		||||
    for (const message of messages) {
 | 
			
		||||
        if (!message.unread) {
 | 
			
		||||
            return;
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        unread_messages.add(message.id);
 | 
			
		||||
@@ -449,7 +449,7 @@ exports.process_loaded_messages = function (messages) {
 | 
			
		||||
        if (message.mentioned_me_directly || is_unmuted_mention) {
 | 
			
		||||
            exports.unread_mentions_counter.add(message.id);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.mark_as_read = function (message_id) {
 | 
			
		||||
@@ -584,18 +584,24 @@ exports.initialize = function () {
 | 
			
		||||
    exports.unread_pm_counter.set_huddles(unread_msgs.huddles);
 | 
			
		||||
    exports.unread_pm_counter.set_pms(unread_msgs.pms);
 | 
			
		||||
    exports.unread_topic_counter.set_streams(unread_msgs.streams);
 | 
			
		||||
    _.each(unread_msgs.mentions, message_id => exports.unread_mentions_counter.add(message_id));
 | 
			
		||||
 | 
			
		||||
    _.each(unread_msgs.huddles, function (obj) {
 | 
			
		||||
        _.each(obj.unread_message_ids, message_id => unread_messages.add(message_id));
 | 
			
		||||
    });
 | 
			
		||||
    _.each(unread_msgs.pms, function (obj) {
 | 
			
		||||
        _.each(obj.unread_message_ids, message_id => unread_messages.add(message_id));
 | 
			
		||||
    });
 | 
			
		||||
    _.each(unread_msgs.streams, function (obj) {
 | 
			
		||||
        _.each(obj.unread_message_ids, message_id => unread_messages.add(message_id));
 | 
			
		||||
    });
 | 
			
		||||
    _.each(unread_msgs.mentions, message_id => unread_messages.add(message_id));
 | 
			
		||||
    for (const message_id of unread_msgs.mentions) {
 | 
			
		||||
        exports.unread_mentions_counter.add(message_id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (const obj of unread_msgs.huddles) {
 | 
			
		||||
        for (const message_id of obj.unread_message_ids) {unread_messages.add(message_id);}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (const obj of unread_msgs.pms) {
 | 
			
		||||
        for (const message_id of obj.unread_message_ids) {unread_messages.add(message_id);}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (const obj of unread_msgs.streams) {
 | 
			
		||||
        for (const message_id of obj.unread_message_ids) {unread_messages.add(message_id);}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (const message_id of unread_msgs.mentions) {unread_messages.add(message_id);}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
window.unread = exports;
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,7 @@ exports.process_read_messages_event = function (message_ids) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _.each(message_ids, function (message_id) {
 | 
			
		||||
    for (const message_id of message_ids) {
 | 
			
		||||
        if (current_msg_list === message_list.narrowed) {
 | 
			
		||||
            // I'm not sure this entirely makes sense for all server
 | 
			
		||||
            // notifications.
 | 
			
		||||
@@ -46,7 +46,7 @@ exports.process_read_messages_event = function (message_ids) {
 | 
			
		||||
        if (message) {
 | 
			
		||||
            process_newly_read_message(message, options);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    unread_ui.update_unread_counts();
 | 
			
		||||
};
 | 
			
		||||
@@ -63,14 +63,14 @@ exports.notify_server_messages_read = function (messages, options) {
 | 
			
		||||
 | 
			
		||||
    message_flags.send_read(messages);
 | 
			
		||||
 | 
			
		||||
    _.each(messages, function (message) {
 | 
			
		||||
    for (const message of messages) {
 | 
			
		||||
        if (current_msg_list === message_list.narrowed) {
 | 
			
		||||
            unread.set_messages_read_in_narrow(true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        unread.mark_as_read(message.id);
 | 
			
		||||
        process_newly_read_message(message, options);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    unread_ui.update_unread_counts();
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -71,22 +71,24 @@ exports.is_member_of = function (user_group_id, user_id) {
 | 
			
		||||
 | 
			
		||||
exports.add_members = function (user_group_id, user_ids) {
 | 
			
		||||
    const user_group = user_group_by_id_dict.get(user_group_id);
 | 
			
		||||
    _.each(user_ids, function (user_id) {
 | 
			
		||||
 | 
			
		||||
    for (const user_id of user_ids) {
 | 
			
		||||
        user_group.members.add(user_id);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.remove_members = function (user_group_id, user_ids) {
 | 
			
		||||
    const user_group = user_group_by_id_dict.get(user_group_id);
 | 
			
		||||
    _.each(user_ids, function (user_id) {
 | 
			
		||||
 | 
			
		||||
    for (const user_id of user_ids) {
 | 
			
		||||
        user_group.members.delete(user_id);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.initialize = function () {
 | 
			
		||||
    _.each(page_params.realm_user_groups, function (user_group) {
 | 
			
		||||
    for (const user_group of page_params.realm_user_groups) {
 | 
			
		||||
        exports.add(user_group);
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    delete page_params.realm_user_groups; // We are the only consumer of this.
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user