/* This library implements two related, similar concepts: - condensing, i.e. cutting off messages taller than about a half screen so that they aren't distractingly tall (and offering a button to uncondense them). - Collapsing, i.e. taking a message and reducing its height to a single line, with a button to see the content. */ var Dict = require('./dict').Dict; var condense = (function () { var exports = {}; var _message_content_height_cache = new Dict(); function show_more_link(row) { row.find(".message_condenser").hide(); row.find(".message_expander").show(); } function show_condense_link(row) { row.find(".message_expander").hide(); row.find(".message_condenser").show(); } function condense_row(row) { var content = row.find(".message_content"); content.addClass("condensed"); show_more_link(row); } function uncondense_row(row) { var content = row.find(".message_content"); content.removeClass("condensed"); show_condense_link(row); } exports.uncollapse = function (row) { // Uncollapse a message, restoring the condensed message [More] or // [Condense] link if necessary. var message = current_msg_list.get(rows.id(row)); message.collapsed = false; message_flags.save_uncollapsed(message); var process_row = function process_row(row) { var content = row.find(".message_content"); content.removeClass("collapsed"); if (message.condensed === true) { // This message was condensed by the user, so re-show the // [More] link. condense_row(row); } else if (message.condensed === false) { // This message was un-condensed by the user, so re-show the // [Condense] link. uncondense_row(row); } else if (content.hasClass("could-be-condensed")) { // By default, condense a long message. condense_row(row); } else { // This was a short message, no more need for a [More] link. row.find(".message_expander").hide(); } }; // We also need to collapse this message in the home view var home_row = home_msg_list.get_row(rows.id(row)); process_row(row); process_row(home_row); }; exports.collapse = function (row) { // Collapse a message, hiding the condensed message [More] or // [Condense] link if necessary. var message = current_msg_list.get(rows.id(row)); message.collapsed = true; if (message.locally_echoed) { // Trying to collapse a locally echoed message is // very rare, and in our current implementation the // server response overwrites the flag, so we just // punt for now. return; } message_flags.save_collapsed(message); var process_row = function process_row(row) { row.find(".message_content").addClass("collapsed"); show_more_link(row); }; // We also need to collapse this message in the home view var home_row = home_msg_list.get_row(rows.id(row)); process_row(row); process_row(home_row); }; exports.toggle_collapse = function (message) { if (message.is_me_message) { // Disabled temporarily because /me messages don't have a // styling for collapsing /me messages (they only recently // added multi-line support). See also popovers.js. return; } // This function implements a multi-way toggle, to try to do what // the user wants for messages: // // * If the message is currently showing any [More] link, either // because it was previously condensed or collapsed, fully display it. // * If the message is fully visible, either because it's too short to // condense or because it's already uncondensed, collapse it var row = current_msg_list.get_row(message.id); if (!row) { return; } var content = row.find(".message_content"); var is_condensable = content.hasClass("could-be-condensed"); var is_condensed = content.hasClass("condensed"); if (message.collapsed) { if (is_condensable) { message.condensed = true; content.addClass("condensed"); exports.show_message_expander(row); row.find(".message_condenser").hide(); } condense.uncollapse(row); } else { if (is_condensed) { message.condensed = false; content.removeClass("condensed"); exports.hide_message_expander(row); row.find(".message_condenser").show(); } else { condense.collapse(row); } } }; exports.clear_message_content_height_cache = function () { _message_content_height_cache = new Dict(); }; exports.un_cache_message_content_height = function (message_id) { _message_content_height_cache.del(message_id); }; function get_message_height(elem, message_id) { if (_message_content_height_cache.has(message_id)) { return _message_content_height_cache.get(message_id); } // shown to be ~2.5x faster than Node.getBoundingClientRect(). var height = elem.offsetHeight; _message_content_height_cache.set(message_id, height); return height; } exports.hide_message_expander = function (row) { if (row.find(".could-be-condensed").length !== 0) { row.find(".message_expander").hide(); } }; exports.show_message_expander = function (row) { if (row.find(".could-be-condensed").length !== 0) { row.find(".message_expander").show(); } }; exports.condense_and_collapse = function (elems) { var height_cutoff = message_viewport.height() * 0.65; _.each(elems, function (elem) { var content = $(elem).find(".message_content"); var message = current_msg_list.get(rows.id($(elem))); if (content !== undefined && message !== undefined) { var message_height = get_message_height(elem, message.id); var long_message = message_height > height_cutoff; if (long_message) { // All long messages are flagged as such. content.addClass("could-be-condensed"); } else { content.removeClass("could-be-condensed"); } // If message.condensed is defined, then the user has manually // specified whether this message should be expanded or condensed. if (message.condensed === true) { condense_row($(elem)); return; } else if (message.condensed === false) { uncondense_row($(elem)); return; } else if (long_message) { // By default, condense a long message. condense_row($(elem)); } else { content.removeClass('condensed'); $(elem).find(".message_expander").hide(); } // Completely hide the message and replace it with a [More] // link if the user has collapsed it. if (message.collapsed) { content.addClass("collapsed"); $(elem).find(".message_expander").show(); } } }); }; exports.initialize = function () { $("#home").on("click", ".message_expander", function () { // Expanding a message can mean either uncollapsing or // uncondensing it. var row = $(this).closest(".message_row"); var message = current_msg_list.get(rows.id(row)); var content = row.find(".message_content"); if (message.collapsed) { // Uncollapse. exports.uncollapse(row); } else if (content.hasClass("condensed")) { // Uncondense (show the full long message). message.condensed = false; content.removeClass("condensed"); $(this).hide(); row.find(".message_condenser").show(); } }); $("#home").on("click", ".message_condenser", function () { var row = $(this).closest(".message_row"); current_msg_list.get(rows.id(row)).condensed = true; condense_row(row); }); }; return exports; }()); if (typeof module !== 'undefined') { module.exports = condense; } window.condense = condense;