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