diff --git a/app/assets/javascripts/mentions.js b/app/assets/javascripts/mentions.js index b6e230437..851337e38 100644 --- a/app/assets/javascripts/mentions.js +++ b/app/assets/javascripts/mentions.js @@ -3,12 +3,31 @@ var Mentions = { return mentionsInput.mentionsInput(Mentions.options); }, + // pre-fetch the list of contacts for the current user. + // called by the initializer of the publisher, for faster ('offline') + // execution of the filtering for mentions fetchContacts : function(){ Mentions.contacts || $.getJSON("/contacts", function(data) { - Mentions.contacts = data; + Mentions.contacts = Mentions.createList(data); }); }, + // creates a list of mentions out of a list of contacts + // @see _contactToMention + createList: function(contacts) { + return _.map(contacts, Mentions._contactToMention); + }, + + // takes a given contact object and modifies to fit the format + // expected by the jQuery.mentionsInput plugin. + // @see http://podio.github.com/jquery-mentions-input/ + _contactToMention: function(contact) { + contact.value = contact.name; + return contact; + }, + + // default options for jQuery.mentionsInput + // @see http://podio.github.com/jquery-mentions-input/ options: { elastic: false, minChars: 1, @@ -20,7 +39,7 @@ var Mentions = { }, templates: { - mentionItemSyntax: _.template("@{<%= mention.name %> ; <%= mention.handle %>}") + mentionItemSyntax: _.template("@{<%= name %> ; <%= handle %>}") } } }; diff --git a/app/views/people/show.html.haml b/app/views/people/show.html.haml index 206cfad3c..6ee3f982a 100644 --- a/app/views/people/show.html.haml +++ b/app/views/people/show.html.haml @@ -6,7 +6,7 @@ - content_for :head do = javascript_include_tag :people :javascript - Mentions.options.prefillMention = #{@person.to_json}; + Mentions.options.prefillMention = Mentions._contactToMention(#{@person.to_json}); - content_for :page_title do = @person.name diff --git a/features/step_definitions/custom_web_steps.rb b/features/step_definitions/custom_web_steps.rb index 298ba941c..11cc641ed 100644 --- a/features/step_definitions/custom_web_steps.rb +++ b/features/step_definitions/custom_web_steps.rb @@ -38,8 +38,9 @@ Then /^the publisher should be expanded$/ do end When /^I append "([^"]*)" to the publisher$/ do |stuff| - previous_value = page.find("#status_message_fake_text").value - fill_in "status_message_fake_text", :with => previous_value + " " + stuff + elem = find('#status_message_fake_text') + elem.native.send_keys ' ' + stuff + wait_until do page.find("#status_message_text").value.match(/#{stuff}/) end diff --git a/features/step_definitions/debug_steps.rb b/features/step_definitions/debug_steps.rb index 2d5e17d43..58a36d533 100644 --- a/features/step_definitions/debug_steps.rb +++ b/features/step_definitions/debug_steps.rb @@ -1,12 +1,21 @@ +module DebuggingCukeHelpers + def start_debugging + require 'ruby-debug' + debugger + true + end +end + +World(DebuggingCukeHelpers) + + When 'I debug' do - require 'ruby-debug' - debugger - true + start_debugging end When /^I wait for (\d+) seconds?$/ do |seconds| sleep seconds.to_i - warn "DELETEME - this step is for debugging only\n" + warn "\nDELETEME - this step is for debugging, only!\n" end When /^I open the error console$/ do diff --git a/vendor/assets/javascripts/jquery.mentionsInput.js b/vendor/assets/javascripts/jquery.mentionsInput.js index 274f24404..40efd9db0 100644 --- a/vendor/assets/javascripts/jquery.mentionsInput.js +++ b/vendor/assets/javascripts/jquery.mentionsInput.js @@ -1,6 +1,6 @@ /* * Mentions Input - * Version 1.0 + * Version 1.0.2 * Written by: Kenneth Auchenberg (Podio) * * Using underscore.js @@ -28,8 +28,8 @@ autocompleteListItemAvatar : _.template(''), autocompleteListItemIcon : _.template('
'), mentionsOverlay : _.template('
'), - mentionItemSyntax : _.template('@[<%= name %>](<%= type %>:<%= id %>)'), - mentionItemHighlight : _.template('<%= name %>') + mentionItemSyntax : _.template('@[<%= value %>](<%= type %>:<%= id %>)'), + mentionItemHighlight : _.template('<%= value %>') } }; @@ -56,18 +56,24 @@ domNode.focus(); } } + }, + rtrim: function(string) { + return string.replace(/\s+$/,""); } }; - var MentionsInput = function (input) { - var settings; - var elmInputBox, elmInputWrapper, elmAutocompleteList, elmWrapperBox, elmMentionsOverlay, elmActiveAutoCompleteItem; + var MentionsInput = function (settings) { + + var domInput, elmInputBox, elmInputWrapper, elmAutocompleteList, elmWrapperBox, elmMentionsOverlay, elmActiveAutoCompleteItem; var mentionsCollection = []; + var autocompleteItemCollection = {}; var inputBuffer = []; var currentDataQuery = ''; + settings = $.extend(true, {}, defaultSettings, settings ); + function initTextarea() { - elmInputBox = $(input); + elmInputBox = $(domInput); if (elmInputBox.attr('data-mentions-input') == 'true') { return; @@ -83,16 +89,19 @@ elmInputBox.bind('keypress', onInputBoxKeyPress); elmInputBox.bind('input', onInputBoxInput); elmInputBox.bind('click', onInputBoxClick); + elmInputBox.bind('blur', onInputBoxBlur); - if (settings.elastic) { + // Elastic textareas, internal setting for the Dispora guys + if( settings.elastic ) { elmInputBox.elastic(); } + } function initAutocomplete() { elmAutocompleteList = $(settings.templates.autocompleteList()); elmAutocompleteList.appendTo(elmWrapperBox); - elmAutocompleteList.delegate('li', 'click', onAutoCompleteItemClick); + elmAutocompleteList.delegate('li', 'mousedown', onAutoCompleteItemClick); } function initMentionsOverlay() { @@ -100,20 +109,20 @@ elmMentionsOverlay.prependTo(elmWrapperBox); } - function updateNames() { + function updateValues() { var syntaxMessage = getInputBoxValue(); _.each(mentionsCollection, function (mention) { - var textSyntax = settings.templates.mentionItemSyntax({ name : mention.name, type : 'contact', id : mention.id, mention: mention }); - - syntaxMessage = syntaxMessage.replace(mention.name, textSyntax); + var textSyntax = settings.templates.mentionItemSyntax(mention); + syntaxMessage = syntaxMessage.replace(mention.value, textSyntax); }); var mentionText = utils.htmlEncode(syntaxMessage); _.each(mentionsCollection, function (mention) { - var textSyntax = settings.templates.mentionItemSyntax({ name : utils.htmlEncode(mention.name), type : 'contact', id : mention.id, mention : mention }); - var textHighlight = settings.templates.mentionItemHighlight({ name : utils.htmlEncode(mention.name), mention : mention }); + var formattedMention = _.extend({}, mention, {value: utils.htmlEncode(mention.value)}); + var textSyntax = settings.templates.mentionItemSyntax(formattedMention); + var textHighlight = settings.templates.mentionItemHighlight(formattedMention); mentionText = mentionText.replace(textSyntax, textHighlight); }); @@ -133,12 +142,13 @@ var inputText = getInputBoxValue(); mentionsCollection = _.reject(mentionsCollection, function (mention, index) { - return !mention.name || inputText.indexOf(mention.name) == -1; + return !mention.value || inputText.indexOf(mention.value) == -1; }); mentionsCollection = _.compact(mentionsCollection); } function addMention(mention) { + var currentMessage = getInputBoxValue(); // Using a regex to figure out positions @@ -150,9 +160,7 @@ var start = currentMessage.substr(0, startCaretPosition); var end = currentMessage.substr(currentCaretPosition, currentMessage.length); - var startEndIndex = (start + mention.name).length; - - var updatedMessageText = start + mention.name + end; + var startEndIndex = (start + mention.value).length + 1; mentionsCollection.push(mention); @@ -162,8 +170,9 @@ hideAutoComplete(); // Mentions & syntax message + var updatedMessageText = start + mention.value + ' ' + end; elmInputBox.val(updatedMessageText); - updateNames(); + updateValues(); // Set correct focus and selection elmInputBox.focus(); @@ -175,7 +184,8 @@ } function onAutoCompleteItemClick(e) { - var mention = $(this).data("mention"); + var elmTarget = $(this); + var mention = autocompleteItemCollection[elmTarget.attr('data-uid')]; addMention(mention); @@ -186,21 +196,26 @@ resetBuffer(); } + function onInputBoxBlur(e) { + hideAutoComplete(); + } + function onInputBoxInput(e) { - updateNames(); + updateValues(); updateMentionsCollection(); hideAutoComplete(); var triggerCharIndex = _.lastIndexOf(inputBuffer, settings.triggerChar); if (triggerCharIndex > -1) { currentDataQuery = inputBuffer.slice(triggerCharIndex + 1).join(''); + currentDataQuery = utils.rtrim(currentDataQuery); _.defer(_.bind(doSearch, this, currentDataQuery)); } } function onInputBoxKeyPress(e) { - if(e.keyCode != KEY.BACKSPACE) { + if(e.keyCode !== KEY.BACKSPACE) { var typedValue = String.fromCharCode(e.which || e.keyCode); inputBuffer.push(typedValue); } @@ -212,6 +227,14 @@ if (e.keyCode == KEY.LEFT || e.keyCode == KEY.RIGHT || e.keyCode == KEY.HOME || e.keyCode == KEY.END) { // Defer execution to ensure carat pos has changed after HOME/END keys _.defer(resetBuffer); + + // IE9 doesn't fire the oninput event when backspace or delete is pressed. This causes the highlighting + // to stay on the screen whenever backspace is pressed after a highlighed word. This is simply a hack + // to force updateValues() to fire when backspace/delete is pressed in IE9. + if (navigator.userAgent.indexOf("MSIE 9") > -1) { + _.defer(updateValues); + } + return; } @@ -247,7 +270,7 @@ case KEY.RETURN: case KEY.TAB: if (elmActiveAutoCompleteItem && elmActiveAutoCompleteItem.length) { - elmActiveAutoCompleteItem.click(); + elmActiveAutoCompleteItem.trigger('mousedown'); return false; } @@ -273,9 +296,9 @@ elmAutocompleteList.show(); // Filter items that has already been mentioned - var mentionedNames = _.pluck(mentionsCollection, 'name'); + var mentionValues = _.pluck(mentionsCollection, 'value'); results = _.reject(results, function (item) { - return _.include(mentionedNames, item.name); + return _.include(mentionValues, item.name); }); if (!results.length) { @@ -287,15 +310,19 @@ var elmDropDownList = $("