From 4f0f469a8ea3df29ad50bb4b71e42dfd3fcb64f1 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Fri, 20 Sep 2013 13:13:39 -0400 Subject: [PATCH] Factor out compose typeahead into individual functions (imported from commit 293c61d5793ced7792c57713210a34736d851ae8) --- static/js/composebox_typeahead.js | 234 ++++++++++++++++-------------- 1 file changed, 122 insertions(+), 112 deletions(-) diff --git a/static/js/composebox_typeahead.js b/static/js/composebox_typeahead.js index 8ebc4c2e47..8579d07f74 100644 --- a/static/js/composebox_typeahead.js +++ b/static/js/composebox_typeahead.js @@ -183,6 +183,127 @@ exports.split_at_cursor = function (query) { return [query.slice(0, cursor), query.slice(cursor)]; }; +exports.compose_content_begins_typeahead = function (query) { + var q = exports.split_at_cursor(query)[0]; + + var strings = q.split(/[\s*(){}\[\]]/); + if (strings.length < 1) { + return false; + } + var current_token = strings[strings.length-1]; + + // Only start the emoji autocompleter if : is directly after one + // of the whitespace or punctuation chars we split on. + if (current_token[0] === ':') { + // We don't want to match non-emoji emoticons such + // as :P or :-p + // Also, if the user has only typed a colon and nothing after, + // no need to match yet. + if (/^:-?.?$/.test(current_token)) { + return false; + } + this.completing = 'emoji'; + this.token = current_token.substring(1); + return emoji.emojis; + } + + // Don't autocomplete more than this many characters. + var max_chars = 30; + var last_at = q.lastIndexOf('@'); + if (last_at === -1 || last_at < q.length-1 - max_chars) { + return false; // No '@', or too far back + } + + // Only match if the @ follows a space, various punctuation, + // or is at the beginning of the string. + if (last_at > 0 && "\n\t \"'(){}[]".indexOf(q[last_at-1]) === -1) { + return false; + } + + current_token = q.substring(last_at + 1); + if (current_token.length < 1 || current_token.lastIndexOf('*') !== -1) { + return false; + } + + this.completing = 'mention'; + this.token = current_token.substring(current_token.indexOf("@")+1); + var all_item = { + special_item_text: "all (Notify everyone)", + email: "all", + // Always sort above, under the assumption that names will + // be longer and only contain "all" as a substring. + pm_recipient_count: Infinity, + full_name: "all" + }; + return page_params.people_list.concat([all_item]); +}; + +exports.content_highlighter = function (item) { + if (this.completing === 'emoji') { + return " " + item.emoji_name; + } else if (this.completing === 'mention') { + var item_formatted = typeahead_helper.render_person(item); + return typeahead_helper.highlight_with_escaping(this.token, item_formatted); + } +}; + +exports.content_typeahead_selected = function (item) { + var pieces = exports.split_at_cursor(this.query); + var beginning = pieces[0]; + var rest = pieces[1]; + + if (this.completing === 'emoji') { + //leading and trailing spaces are required for emoji, except if it begins a message. + if (beginning.lastIndexOf(":") === 0 || beginning.charAt(beginning.lastIndexOf(":") - 1) === " ") { + beginning = beginning.replace(/:\S+$/, "") + ":" + item.emoji_name + ": "; + } else { + beginning = beginning.replace(/:\S+$/, "") + " :" + item.emoji_name + ": "; + } + } else if (this.completing === 'mention') { + beginning = (beginning.substring(0, beginning.length - this.token.length-1) + + '@**' + item.full_name + '** '); + + // We insert a special `all` item to the autocompleter above + // Don't consider it a user mention + if (item.email !== 'all') { + $(document).trigger('usermention_completed.zulip', {mentioned: item}); + } + } + + // Keep the cursor after the newly inserted text, as Bootstrap will call textbox.change() to overwrite the text + // in the textbox. + setTimeout(function () { + $('#new_message_content').caret(beginning.length, beginning.length); + }, 0); + return beginning + rest; +}; + +exports.initialize_compose_typeahead = function (selector) { + $(selector).typeahead({ + items: 5, + dropup: true, + fixed: true, + source: exports.compose_content_begins_typeahead, + highlighter: exports.content_highlighter, + matcher: function (item) { + if (this.completing === 'emoji') { + return query_matches_emoji(this.token, item); + } else if (this.completing === 'mention') { + return query_matches_person(this.token, item); + } + }, + sorter: function (matches) { + if (this.completing === 'emoji') { + return typeahead_helper.sort_emojis(matches, this.token); + } else if (this.completing === 'mention') { + return typeahead_helper.sort_recipients(matches, this.token); + } + }, + updater: exports.content_typeahead_selected, + stopAdvance: true // Do not advance to the next field on a tab or enter + }); +}; + exports.initialize = function () { select_on_focus("stream"); select_on_focus("subject"); @@ -286,118 +407,7 @@ exports.initialize = function () { stopAdvance: true // Do not advance to the next field on a tab or enter }); - $( "#new_message_content" ).typeahead({ - source: function (query, process) { - var q = exports.split_at_cursor(query)[0]; - - var strings = q.split(/[\s*(){}\[\]]/); - if (strings.length < 1) { - return false; - } - var current_token = strings[strings.length-1]; - - // Only start the emoji autocompleter if : is directly after one - // of the whitespace or punctuation chars we split on. - if (current_token[0] === ':') { - // We don't want to match non-emoji emoticons such - // as :P or :-p - // Also, if the user has only typed a colon and nothing after, - // no need to match yet. - if (/^:-?.?$/.test(current_token)) { - return false; - } - this.completing = 'emoji'; - this.token = current_token.substring(1); - return emoji.emojis; - } - - // Don't autocomplete more than this many characters. - var max_chars = 30; - var last_at = q.lastIndexOf('@'); - if (last_at === -1 || last_at < q.length-1 - max_chars) { - return false; // No '@', or too far back - } - - // Only match if the @ follows a space, various punctuation, - // or is at the beginning of the string. - if (last_at > 0 && "\n\t \"'(){}[]".indexOf(q[last_at-1]) === -1) { - return false; - } - - current_token = q.substring(last_at + 1); - if (current_token.length < 1 || current_token.lastIndexOf('*') !== -1) { - return false; - } - - this.completing = 'mention'; - this.token = current_token.substring(current_token.indexOf("@")+1); - var all_item = { - special_item_text: "all (Notify everyone)", - email: "all", - // Always sort above, under the assumption that names will - // be longer and only contain "all" as a substring. - pm_recipient_count: Infinity, - full_name: "all" - }; - return page_params.people_list.concat([all_item]); - }, - items: 5, - highlighter: function (item) { - if (this.completing === 'emoji') { - return " " + item.emoji_name; - } else if (this.completing === 'mention') { - var item_formatted = typeahead_helper.render_person(item); - return typeahead_helper.highlight_with_escaping(this.token, item_formatted); - } - }, - dropup: true, - fixed: true, - matcher: function (item) { - if (this.completing === 'emoji') { - return query_matches_emoji(this.token, item); - } else if (this.completing === 'mention') { - return query_matches_person(this.token, item); - } - }, - sorter: function (matches) { - if (this.completing === 'emoji') { - return typeahead_helper.sort_emojis(matches, this.token); - } else if (this.completing === 'mention') { - return typeahead_helper.sort_recipients(matches, this.token); - } - }, - updater: function (item) { - var pieces = exports.split_at_cursor(this.query); - var beginning = pieces[0]; - var rest = pieces[1]; - - if (this.completing === 'emoji') { - //leading and trailing spaces are required for emoji, except if it begins a message. - if (beginning.lastIndexOf(":") === 0 || beginning.charAt(beginning.lastIndexOf(":") - 1) === " ") { - beginning = beginning.replace(/:\S+$/, "") + ":" + item.emoji_name + ": "; - } else { - beginning = beginning.replace(/:\S+$/, "") + " :" + item.emoji_name + ": "; - } - } else if (this.completing === 'mention') { - beginning = (beginning.substring(0, beginning.length - this.token.length-1) - + '@**' + item.full_name + '** '); - - // We insert a special `all` item to the autocompleter above - // Don't consider it a user mention - if (item.email !== 'all') { - $(document).trigger('usermention_completed.zulip', {mentioned: item}); - } - } - - // Keep the cursor after the newly inserted text, as Bootstrap will call textbox.change() to overwrite the text - // in the textbox. - setTimeout(function () { - $('#new_message_content').caret(beginning.length, beginning.length); - }, 0); - return beginning + rest; - }, - stopAdvance: true // Do not advance to the next field on a tab or enter - }); + exports.initialize_compose_typeahead("#new_message_content"); $( "#private_message_recipient" ).blur(function (event) { var val = $(this).val();