From 3efc31c68f511a3a4a9b4bb8a27fa333873e7663 Mon Sep 17 00:00:00 2001 From: danielvincent <danielgrippi@gmail.com> Date: Fri, 4 Feb 2011 17:26:21 -0800 Subject: [PATCH] mention js working minus tracking keypresses with the hidden message field. --- app/models/person.rb | 2 +- app/models/status_message.rb | 2 +- public/javascripts/publisher.js | 79 +++++++++++++++--- spec/javascripts/publisher-spec.js | 128 +++++++++++++++++++---------- 4 files changed, 152 insertions(+), 59 deletions(-) diff --git a/app/models/person.rb b/app/models/person.rb index 82f0c81d98..4f360e81ef 100644 --- a/app/models/person.rb +++ b/app/models/person.rb @@ -160,7 +160,7 @@ class Person < ActiveRecord::Base end def as_json(opts={}) - {:id => self.guid, :name => self.name, :avatar => self.profile.image_url(:thumb_small), :url => "/people/#{self.id}"} + {:id => self.guid, :name => self.name, :avatar => self.profile.image_url(:thumb_small), :handle => self.diaspora_handle, :url => "/people/#{self.id}"} end protected diff --git a/app/models/status_message.rb b/app/models/status_message.rb index 3a8b6935e0..d229602a41 100644 --- a/app/models/status_message.rb +++ b/app/models/status_message.rb @@ -37,7 +37,7 @@ class StatusMessage < Post identifiers = self.message.scan(regex).map do |match| match.last end - Person.where(:diaspora_handle => identifiers) + self.person.owner.contact_people.where(:diaspora_handle => identifiers) end def to_activity diff --git a/public/javascripts/publisher.js b/public/javascripts/publisher.js index 527b356b8f..0e79c3a3cc 100644 --- a/public/javascripts/publisher.js +++ b/public/javascripts/publisher.js @@ -36,11 +36,6 @@ var Publisher = { return Publisher.cachedHiddenInput; }, - appendToHiddenField: function(evt){ - Publisher.hiddenInput().val( - Publisher.input().val()); - }, - autocompletion: { options : function(){return { minChars : 1, @@ -58,13 +53,71 @@ var Publisher = { return row.name; } };}, + hiddenMentionFromPerson : function(personData){ + return "@{" + personData.name + "; " + personData.handle + "}"; + }, - onSelect : function(input, data, formatted) { - addMentionToVisibleInput(input, formatted); + onSelect : function(visibleInput, data, formatted) { + var visibleCursorIndex = visibleInput[0].selectionStart; + var visibleLoc = Publisher.autocompletion.addMentionToInput(visibleInput, visibleCursorIndex, formatted); + + + var hiddenCursorIndex = visibleCursorIndex + Publisher.autocompletion.mentionList.offsetFrom(visibleCursorIndex); + var hiddenLoc = Publisher.autocompletion.addMentionToInput(Publisher.hiddenInput(), hiddenCursorIndex, Publisher.autocompletion.hiddenMentionFromPerson(data)); + var mention = { visibleStart: visibleLoc[0], + visibleEnd : visibleLoc[1], + hiddenStart : hiddenLoc[0], + hiddenEnd : hiddenLoc[1] + }; + }, + + mentionList : { + mentions : [], + push : function(mention){ + mention.offset = mention.hiddenEnd - mention.visibleEnd; + this.mentions.push(mention); + }, + keypressAt : function(visibleCursorIndex){ + var mentionIndex = this.mentionAt(visibleCursorIndex); + var mention = this.mentions[mentionIndex]; + if(!mention){return;} + var visibleMentionString = Publisher.input().val().slice(mention.visibleStart, mention.visibleEnd); + var hiddenContent = Publisher.hiddenInput().val(); + hiddenContent = hiddenContent.slice(0,mention.hiddenStart) + + visibleMentionString + + hiddenContent.slice(mention.hiddenEnd); + Publisher.hiddenInput().val(hiddenContent); + + this.mentions.splice(mentionIndex, 1); + }, + mentionAt : function(visibleCursorIndex){ + for(i in this.mentions){ + var mention = this.mentions[i]; + if(visibleCursorIndex >= mention.visibleStart && visibleCursorIndex < mention.visibleEnd){ + return i; + } + return false; + } + }, + offsetFrom: function(visibleCursorIndex){ + var mention = {visibleStart : -1, fake: true}; + var currentMention; + for(i in this.mentions){ + currentMention = this.mentions[i]; + if(visibleCursorIndex >= currentMention.visibleStart && + currentMention.visibleStart > mention.visibleStart){ + mention = currentMention; + } + } + if(mention && !mention.fake){ + return mention.offset; + }else{ + return 0; + } + } }, - addMentionToVisibleInput: function(input, formatted){ - var cursorIndex = input[0].selectionStart; + addMentionToInput: function(input, cursorIndex, formatted){ var inputContent = input.val(); var stringLoc = Publisher.autocompletion.findStringToReplace(input.val(), cursorIndex); @@ -73,12 +126,13 @@ var Publisher = { var stringEnd = inputContent.slice(stringLoc[1]); input.val(stringStart + formatted + stringEnd); + return [stringStart.length, stringStart.length + stringLoc[1]] }, findStringToReplace: function(value, cursorIndex){ var atLocation = value.lastIndexOf('@', cursorIndex); if(atLocation == -1){return [0,0];} - var nextAt = value.indexOf('@', cursorIndex+1); + var nextAt = value.indexOf(' @', cursorIndex+1); if(nextAt == -1){nextAt = value.length;} return [atLocation, nextAt]; @@ -115,6 +169,7 @@ var Publisher = { initialize: function() { Publisher.cachedForm = false; Publisher.cachedInput = false; + Publisher.cachedHiddenInput = false; $("div.public_toggle input").live("click", function(evt) { $("#publisher_service_icons").toggleClass("dim"); if ($(this).attr('checked') == true) { @@ -127,9 +182,7 @@ var Publisher = { }; Publisher.autocompletion.initialize(); - Publisher.updateHiddenField(); - Publisher.form().find('#status_message_fake_message').bind('keydown', - Publisher.updateHiddenField); + Publisher.hiddenInput().val(Publisher.input().val()); Publisher.form().find("textarea").bind("focus", function(evt) { Publisher.open(); $(this).css('min-height', '42px'); diff --git a/spec/javascripts/publisher-spec.js b/spec/javascripts/publisher-spec.js index e48aa9da59..4ba86e7e98 100644 --- a/spec/javascripts/publisher-spec.js +++ b/spec/javascripts/publisher-spec.js @@ -6,21 +6,6 @@ describe("Publisher", function() { describe("initialize", function(){ - it("calls updateHiddenField", function(){ - spec.loadFixture('aspects_index_prefill'); - spyOn(Publisher, 'updateHiddenField'); - Publisher.initialize(); - expect(Publisher.updateHiddenField).toHaveBeenCalled(); - }); - - 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').keydown(); - expect(Publisher.updateHiddenField.mostRecentCall.args[0].type).toBe('keydown'); - }); - it("calls close when it does not have text", function(){ spec.loadFixture('aspects_index'); spyOn(Publisher, 'close'); @@ -67,17 +52,6 @@ describe("Publisher", function() { expect(Publisher.form().find(".options_and_submit:visible").length).toBe(0); }); }); - describe("updateHiddenField", function(){ - beforeEach(function(){ - spec.loadFixture('aspects_index_prefill'); - }); - - it("copies the value of fake_message to message",function(){ - Publisher.updateHiddenField(); - expect(Publisher.form().find('#status_message_message').val()).toBe( - Publisher.form().find('#status_message_fake_message').val()); - }); - }); describe("input", function(){ beforeEach(function(){ spec.loadFixture('aspects_index_prefill'); @@ -88,6 +62,8 @@ describe("Publisher", function() { }); }); describe("autocompletion", function(){ + describe("onKeypress", function(){ + });, describe("searchTermFromValue", function(){ var func; beforeEach(function(){func = Publisher.autocompletion.searchTermFromValue;}); @@ -124,56 +100,120 @@ describe("Publisher", function() { describe("onSelect", function(){ }); - describe("addMentionToHiddenInput", function(){ - var func; - var input; + describe("mentionList", function(){ + var visibleInput, visibleVal, + hiddenInput, hiddenVal, + list, + func, + mention; beforeEach(function(){ spec.loadFixture('aspects_index'); - func = Publisher.autocompletion.addMentionToHiddenInput; - input = Publisher.input(); + list = Publisher.autocompletion.mentionList; + func = list.keypressAt; + visibleInput = Publisher.input(); + hiddenInput = Publisher.hiddenInput(); + mention = { visibleStart : 0, + visibleEnd : 5, + hiddenStart : 0, + hiddenEnd : 21 + }; + list.mentions = []; + list.push(mention); + visibleVal = "Danny loves testing javascript"; + visibleInput.val(visibleVal); + hiddenVal = "@{Danny; dan@pod.org} loves testing javascript"; + hiddenInput.val(hiddenVal); + }); + describe("push", function(){ + it("adds mention to mentions array", function(){ + expect(list.mentions.length).toBe(1); + expect(list.mentions[0]).toBe(mention) + }); + }); + describe("mentionAt", function(){ + it("returns the location of the mention at that location in the mentions array", function(){ + expect(list.mentions[list.mentionAt(3)]).toBe(mention); + }); + it("returns null if there is no mention", function(){ + expect(list.mentionAt(8)).toBeFalsy(); + }); + }); + describe("keypressAt", function(){ + it("does nothing if there is no visible mention at that index", function(){ + list.keypressAt(8); + expect(visibleInput.val()).toBe(visibleVal) + expect(hiddenInput.val()).toBe(hiddenVal) + }); + it("deletes the mention from the hidden field if there is a mention", function(){ + list.keypressAt(3); + expect(visibleInput.val()).toBe(visibleVal) + expect(hiddenInput.val()).toBe(visibleVal) + }); + it("deletes the mention from the list", function(){ + list.keypressAt(3); + expect(list.mentionAt(3)).toBeFalsy(); + }); + it("updates the offsets of the remaining mentions in the list"); + }); + describe("offsetFrom", function(){ + var func; + beforeEach(function(){ + func = list.offsetFrom; + }); + it("returns the offset of the mention at that location", function(){ + expect(list.offsetFrom(3)).toBe(mention.offset); + }); + it("returns the offset of the previous mention if there is no mention there", function(){ + expect(list.offsetFrom(10)).toBe(mention.offset); + }); + it("returns 0 if there are no mentions", function(){ + list.mentions = []; + expect(list.offsetFrom(8)).toBe(0); + }); }); - }); - describe("addMentionToVisibleInput", function(){ + + + describe("addMentionToInput", function(){ var func; var input; var replaceWith; beforeEach(function(){ spec.loadFixture('aspects_index'); - func = Publisher.autocompletion.addMentionToVisibleInput; + func = Publisher.autocompletion.addMentionToInput; 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; + var cursorIndex = 13; + func(input, cursorIndex, replaceWith); 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); + var cursorIndex = 7; + func(input, cursorIndex, 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); + var cursorIndex = 9; + func(input, cursorIndex, 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); + var cursorIndex = 8; + func(input, cursorIndex, 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); + var cursorIndex = 15; + func(input, cursorIndex, replaceWith); expect(input.val()).toBe('@asod asdo ' + replaceWith); }); }); -- GitLab