diff --git a/frontend_tests/node_tests/compose.js b/frontend_tests/node_tests/compose.js index 266480cdb6..fa60e9274d 100644 --- a/frontend_tests/node_tests/compose.js +++ b/frontend_tests/node_tests/compose.js @@ -7,8 +7,9 @@ set_global('page_params', { set_global('navigator', {}); set_global('document', { - location: { - }, + getElementById: function () { return $('#compose-textarea'); }, + execCommand: function () { return false; }, + location: {}, }); set_global('channel', {}); set_global('templates', {}); @@ -256,6 +257,107 @@ people.add(bob); assert($("#compose-all-everyone").visible()); }()); +(function test_markdown_shortcuts() { + blueslip.error = noop; + blueslip.log = noop; + + var queryCommandEnabled = true; + var event = { + keyCode: 66, + target: { + id: 'compose-textarea', + }, + stopPropagation: noop, + preventDefault: noop, + }; + var input_text = ""; + var range_start = 0; + var range_length = 0; + var compose_value = $("#compose_textarea").val(); + var selected_word = ""; + + global.document.queryCommandEnabled = function () { + return queryCommandEnabled; + }; + global.document.execCommand = function (cmd, bool, markdown) { + var compose_textarea = $("#compose-textarea"); + var value = compose_textarea.val(); + $("#compose-textarea").val(value.substring(0, compose_textarea.range().start)+ + markdown+value.substring(compose_textarea.range().end, value.length)); + }; + + $("#compose-textarea").range = function () { + return { + start: range_start, + end: range_start + range_length, + length: range_length, + range: noop, + text: $("#compose-textarea").val().substring(range_start, range_length+range_start), + }; + }; + $('#compose-textarea').caret = noop; + + // Test bold: ctrl/cmd + b. + input_text = "Anything bold."; + $("#compose-textarea").val(input_text); + compose_value = $("#compose-textarea").val(); + // Select "bold" word in compose box. + selected_word = "bold"; + range_start = compose_value.search(selected_word); + range_length = selected_word.length; + event.keyCode = 66; + event.metaKey = false; + event.ctrlKey = true; + compose.handle_keydown(event); + assert.equal("Anything **bold**.", $('#compose-textarea').val()); + // Test if no text is selected. + // Change cursor to first position. + range_start = 0; + range_length = 0; + compose.handle_keydown(event); + assert.equal("****Anything **bold**.", $('#compose-textarea').val()); + + // Test italic: ctrl/cmd + i. + input_text = "Anything italic"; + $("#compose-textarea").val(input_text); + $("#compose-textarea").val(input_text); + compose_value = $("#compose-textarea").val(); + // Select "italic" word in compose box. + selected_word = "italic"; + range_start = compose_value.search(selected_word); + range_length = selected_word.length; + event.keyCode = undefined; + event.which = 73; + event.metaKey = true; + event.ctrlKey = false; + compose.handle_keydown(event); + assert.equal("Anything *italic*", $('#compose-textarea').val()); + // Test if no text is selected. + range_length = 0; + // Change cursor to first position. + range_start = 0; + compose.handle_keydown(event); + assert.equal("**Anything *italic*", $('#compose-textarea').val()); + + // Test link insertion: ctrl/cmd + l. + input_text = "Any link."; + $("#compose-textarea").val(input_text); + compose_value = $("#compose-textarea").val(); + // Select "link" word in compose box. + selected_word = "link"; + range_start = compose_value.search(selected_word); + range_length = selected_word.length; + event.keyCode = 76; + event.which = undefined; + event.ctrlKey = true; + compose.handle_keydown(event); + assert.equal("Any [link](url).", $('#compose-textarea').val()); + // Test if exec command is not enabled in browser. + queryCommandEnabled = false; + compose.handle_keydown(event); + +}()); + (function test_send_message_success() { blueslip.error = noop; blueslip.log = noop; diff --git a/static/js/compose.js b/static/js/compose.js index 8b40474b60..40bc3d94a5 100644 --- a/static/js/compose.js +++ b/static/js/compose.js @@ -555,9 +555,66 @@ exports.validate = function () { return validate_stream_message(); }; +exports.handle_keydown = function (event) { + var code = event.keyCode || event.which; + var textarea = $("#compose-textarea"); + var range = textarea.range(); + var bKey = 66; + var iKey = 73; + var lKey = 76; + + if ((code === bKey || code === iKey || code === lKey) && (event.ctrlKey || event.metaKey)) { + function add_markdown(markdown) { + var textarea = $("#compose-textarea"); + var range = textarea.range(); + if (!document.execCommand('insertText', false, markdown)) { + textarea.range(range.start, range.end).range(markdown); + } + event.preventDefault(); + } + + if (code === bKey) { + // ctrl + b: Convert selected text to bold text + add_markdown("**" + range.text + "**"); + if (!range.length) { + textarea.caret(textarea.caret() - 2); + } + } + if (code === iKey) { + // ctrl + i: Convert selected text to italic text + add_markdown("*" + range.text + "*"); + if (!range.length) { + textarea.caret(textarea.caret() - 1); + } + } + if (code === lKey) { + // ctrl + l: Insert a link to selected text + add_markdown("[" + range.text + "](url)"); + var position = textarea.caret(); + var txt = document.getElementById("compose-textarea"); + + // Include selected text in between [] parantheses and insert '(url)' + // where "url" should be automatically selected. + // Position of cursor depends on whether browser supports exec + // command or not. So set cursor position accrodingly. + if (document.queryCommandEnabled('insertText')) { + txt.selectionStart = position - 4; + txt.selectionEnd = position - 1; + } else { + txt.selectionStart = position + range.length + 3; + txt.selectionEnd = position + range.length + 6; + } + } + + compose_ui.autosize_textarea(); + return; + } +}; + exports.initialize = function () { $('#stream,#subject,#private_message_recipient').on('keyup', update_fade); $('#stream,#subject,#private_message_recipient').on('change', update_fade); + $('#compose-textarea').on('keydown', exports.handle_keydown); $("#compose form").on("submit", function (e) { e.preventDefault();