From 7d21f7bfdd97a9082679d3c2c207971df709a01d Mon Sep 17 00:00:00 2001 From: danielvincent <danielgrippi@gmail.com> Date: Fri, 4 Feb 2011 14:09:44 -0800 Subject: [PATCH] publisher autocomplete correctly adds name into status_message_fake textarea --- .../javascripts/jquery.autocomplete-custom.js | 23 ++++--- public/javascripts/publisher.js | 60 +++++++++++++----- public/javascripts/search.js | 2 +- spec/javascripts/publisher-spec.js | 63 ++++++++++++++++++- 4 files changed, 122 insertions(+), 26 deletions(-) diff --git a/public/javascripts/jquery.autocomplete-custom.js b/public/javascripts/jquery.autocomplete-custom.js index 5208f5822f..afb50fa515 100644 --- a/public/javascripts/jquery.autocomplete-custom.js +++ b/public/javascripts/jquery.autocomplete-custom.js @@ -13,7 +13,6 @@ ;(function($) { - function lastWord(s){return s;} //Fuck you $.fn.extend({ autocomplete: function(urlOrData, options) { var isUrl = typeof urlOrData == "string"; @@ -148,6 +147,7 @@ $.Autocompleter = function(input, options) { break; default: + options.onLetterTyped(event, $input); clearTimeout(timeout); timeout = setTimeout(onChange, options.delay); break; @@ -215,9 +215,8 @@ $.Autocompleter = function(input, options) { v += options.multipleSeparator; } - $input.val(v); hideResultsNow(); - $input.trigger("result", [selected.data, selected.value]); + options.onSelect($input, selected.data, selected.value); return true; } @@ -265,9 +264,9 @@ $.Autocompleter = function(input, options) { function autoFill(q, sValue){ // autofill in the complete box w/the first match as long as the user hasn't entered in more data // if the last user key pressed was backspace, don't autofill - if( options.autoFill && (lastWord($input.val()).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE ) { + if( options.autoFill && (options.lastWord($input.val(), null, options.multiple).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE ) { // fill in the value (keep the case the user has typed) - $input.val($input.val() + sValue.substring(lastWord(previousValue).length)); + $input.val($input.val() + sValue.substring(options.lastWord(previousValue, null, options.multiple).length)); // select the portion of the value not typed by the user (so the next character will erase) $.Autocompleter.Selection(input, previousValue.length, previousValue.length + sValue.length); } @@ -340,7 +339,7 @@ $.Autocompleter = function(input, options) { dataType: options.dataType, url: options.url, data: $.extend({ - q: lastWord(term), + q: options.lastWord(term, null, options.multiple), limit: options.max }, extraParams), success: function(data) { @@ -380,10 +379,19 @@ $.Autocompleter = function(input, options) { }; $.Autocompleter.defaults = { - searchTermFromValue: lastWord, + onLetterTyped : function(event){}, + lastWord : function(value, crap, multiple) { + if ( !multiple ) + return value; + var words = trimWords(value); + return words[words.length - 1]; + }, inputClass: "ac_input", resultsClass: "ac_results", loadingClass: "ac_loading", + onSelect: function(input, data, formatted){ + input.val(formatted); + }, minChars: 1, delay: 400, matchCase: false, @@ -407,6 +415,7 @@ $.Autocompleter.defaults = { scroll: true, scrollHeight: 180 }; +$.Autocompleter.defaults.searchTermFromValue = $.Autocompleter.defaults.lastWord; $.Autocompleter.Cache = function(options) { diff --git a/public/javascripts/publisher.js b/public/javascripts/publisher.js index 8a3ded14ed..527b356b8f 100644 --- a/public/javascripts/publisher.js +++ b/public/javascripts/publisher.js @@ -28,18 +28,28 @@ var Publisher = { return Publisher.cachedInput; }, - updateHiddenField: function(evt){ - Publisher.form().find('#status_message_message').val( + cachedHiddenInput : false, + hiddenInput: function(){ + if(!Publisher.cachedHiddenInput){ + Publisher.cachedHiddenInput = Publisher.form().find('#status_message_message'); + } + return Publisher.cachedHiddenInput; + }, + + appendToHiddenField: function(evt){ + Publisher.hiddenInput().val( Publisher.input().val()); }, + autocompletion: { options : function(){return { minChars : 1, max : 5, + onSelect : Publisher.autocompletion.onSelect, searchTermFromValue: Publisher.autocompletion.searchTermFromValue, scroll : false, formatItem: function(row, i, max) { - return row.name; + return "<img src='"+ row.avatar +"' class='avatar'/>" + row.name; }, formatMatch: function(row, i, max) { return row.name; @@ -49,24 +59,44 @@ var Publisher = { } };}, - selectItemCallback : function(event, data, formatted) { - var textarea = Publisher.input(); - textarea.val(formatted); + onSelect : function(input, data, formatted) { + addMentionToVisibleInput(input, formatted); }, - searchTermFromValue: function(value, cursorIndex) - { + addMentionToVisibleInput: function(input, formatted){ + var cursorIndex = input[0].selectionStart; + var inputContent = input.val(); + + var stringLoc = Publisher.autocompletion.findStringToReplace(input.val(), cursorIndex); + + var stringStart = inputContent.slice(0, stringLoc[0]); + var stringEnd = inputContent.slice(stringLoc[1]); + + input.val(stringStart + formatted + stringEnd); + }, + + findStringToReplace: function(value, cursorIndex){ var atLocation = value.lastIndexOf('@', cursorIndex); - if(atLocation == -1){return '';} + if(atLocation == -1){return [0,0];} var nextAt = value.indexOf('@', cursorIndex+1); if(nextAt == -1){nextAt = value.length;} - if(atLocation < 2){ - atLocation = 0; - }else{ atLocation = atLocation -2 } + return [atLocation, nextAt]; + + }, + + searchTermFromValue: function(value, cursorIndex) + { + var stringLoc = Publisher.autocompletion.findStringToReplace(value, cursorIndex); + if(stringLoc[0] <= 2){ + stringLoc[0] = 0; + }else{ + stringLoc[0] -= 2 + } + + var relevantString = value.slice(stringLoc[0], stringLoc[1]).replace(/\s+$/,""); - relevantString = value.slice(atLocation, nextAt).replace(/\s+$/,""); - matches = relevantString.match(/(^|\s)@(.+)/); + var matches = relevantString.match(/(^|\s)@(.+)/); if(matches){ return matches[2]; }else{ @@ -98,7 +128,7 @@ var Publisher = { Publisher.autocompletion.initialize(); Publisher.updateHiddenField(); - Publisher.form().find('#status_message_fake_message').change( + Publisher.form().find('#status_message_fake_message').bind('keydown', Publisher.updateHiddenField); Publisher.form().find("textarea").bind("focus", function(evt) { Publisher.open(); diff --git a/public/javascripts/search.js b/public/javascripts/search.js index b587a95c79..f0d35c11c5 100644 --- a/public/javascripts/search.js +++ b/public/javascripts/search.js @@ -25,6 +25,7 @@ var Search = { }, options : function(){return { minChars : 3, + onSelect: Search.selectItemCallback, max : 5, scroll : false, delay : 200, @@ -49,7 +50,6 @@ var Search = { initialize : function() { $(Search.selector).autocomplete(Search.source, Search.options()); - $(Search.selector).result(Search.selectItemCallback); } } diff --git a/spec/javascripts/publisher-spec.js b/spec/javascripts/publisher-spec.js index 166d64db7e..e48aa9da59 100644 --- a/spec/javascripts/publisher-spec.js +++ b/spec/javascripts/publisher-spec.js @@ -13,12 +13,12 @@ describe("Publisher", function() { expect(Publisher.updateHiddenField).toHaveBeenCalled(); }); - it("attaches updateHiddenField to the change handler on fake_message", function(){ + it("attaches updateHiddenField to the keydown handler on fake_message", function(){ spec.loadFixture('aspects_index_prefill'); spyOn(Publisher, 'updateHiddenField'); Publisher.initialize(); - Publisher.form().find('#status_message_fake_message').change(); - expect(Publisher.updateHiddenField.mostRecentCall.args[0].type).toBe('change'); + Publisher.form().find('#status_message_fake_message').keydown(); + expect(Publisher.updateHiddenField.mostRecentCall.args[0].type).toBe('keydown'); }); it("calls close when it does not have text", function(){ @@ -120,5 +120,62 @@ describe("Publisher", function() { expect(func('@asod asdo @asd asok', 15)).toBe('asd asok'); }); }); + + describe("onSelect", function(){ + + }); + describe("addMentionToHiddenInput", function(){ + var func; + var input; + + beforeEach(function(){ + spec.loadFixture('aspects_index'); + func = Publisher.autocompletion.addMentionToHiddenInput; + input = Publisher.input(); + }); + + }); + + describe("addMentionToVisibleInput", function(){ + var func; + var input; + var replaceWith; + beforeEach(function(){ + spec.loadFixture('aspects_index'); + func = Publisher.autocompletion.addMentionToVisibleInput; + input = Publisher.input(); + replaceWith = "Replace with this."; + }); + it("replaces everything after an @ if the cursor is a word after that @", function(){ + input.val('not @dan grip'); + func(input, replaceWith); + input[0].selectionStart = 13; + expect(input.val()).toBe('not ' + replaceWith); + }); + it("replaces everything after an @ if the cursor is after that @", function(){ + input.val('not @dan grip'); + input[0].selectionStart = 7; + func(input, replaceWith); + expect(input.val()).toBe('not ' + replaceWith); + }); + it("replaces everything after an @ at the start of the line", function(){ + input.val('@dan grip'); + input[0].selectionStart = 9; + func(input, replaceWith); + expect(input.val()).toBe(replaceWith); + }); + it("replaces everything between @s if there are 2 @s and the cursor is between them", function(){ + input.val('@asdpo aoisdj @asodk'); + input[0].selectionStart = 8; + func(input, replaceWith); + expect(input.val()).toBe(replaceWith + ' @asodk'); + }); + it("replaces everything after the 2nd @ if there are 2 @s and the cursor after them", function(){ + input.val('@asod asdo @asd asok'); + input[0].selectionStart = 15; + func(input, replaceWith); + expect(input.val()).toBe('@asod asdo ' + replaceWith); + }); + }); }); }); -- GitLab