mention js working minus tracking keypresses with the hidden message field.
This commit is contained in:
parent
7d21f7bfdd
commit
3efc31c68f
4 changed files with 153 additions and 60 deletions
|
|
@ -160,7 +160,7 @@ class Person < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def as_json(opts={})
|
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
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ class StatusMessage < Post
|
||||||
identifiers = self.message.scan(regex).map do |match|
|
identifiers = self.message.scan(regex).map do |match|
|
||||||
match.last
|
match.last
|
||||||
end
|
end
|
||||||
Person.where(:diaspora_handle => identifiers)
|
self.person.owner.contact_people.where(:diaspora_handle => identifiers)
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_activity
|
def to_activity
|
||||||
|
|
|
||||||
|
|
@ -36,11 +36,6 @@ var Publisher = {
|
||||||
return Publisher.cachedHiddenInput;
|
return Publisher.cachedHiddenInput;
|
||||||
},
|
},
|
||||||
|
|
||||||
appendToHiddenField: function(evt){
|
|
||||||
Publisher.hiddenInput().val(
|
|
||||||
Publisher.input().val());
|
|
||||||
},
|
|
||||||
|
|
||||||
autocompletion: {
|
autocompletion: {
|
||||||
options : function(){return {
|
options : function(){return {
|
||||||
minChars : 1,
|
minChars : 1,
|
||||||
|
|
@ -58,13 +53,71 @@ var Publisher = {
|
||||||
return row.name;
|
return row.name;
|
||||||
}
|
}
|
||||||
};},
|
};},
|
||||||
|
hiddenMentionFromPerson : function(personData){
|
||||||
onSelect : function(input, data, formatted) {
|
return "@{" + personData.name + "; " + personData.handle + "}";
|
||||||
addMentionToVisibleInput(input, formatted);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
addMentionToVisibleInput: function(input, formatted){
|
onSelect : function(visibleInput, data, formatted) {
|
||||||
var cursorIndex = input[0].selectionStart;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
addMentionToInput: function(input, cursorIndex, formatted){
|
||||||
var inputContent = input.val();
|
var inputContent = input.val();
|
||||||
|
|
||||||
var stringLoc = Publisher.autocompletion.findStringToReplace(input.val(), cursorIndex);
|
var stringLoc = Publisher.autocompletion.findStringToReplace(input.val(), cursorIndex);
|
||||||
|
|
@ -73,12 +126,13 @@ var Publisher = {
|
||||||
var stringEnd = inputContent.slice(stringLoc[1]);
|
var stringEnd = inputContent.slice(stringLoc[1]);
|
||||||
|
|
||||||
input.val(stringStart + formatted + stringEnd);
|
input.val(stringStart + formatted + stringEnd);
|
||||||
|
return [stringStart.length, stringStart.length + stringLoc[1]]
|
||||||
},
|
},
|
||||||
|
|
||||||
findStringToReplace: function(value, cursorIndex){
|
findStringToReplace: function(value, cursorIndex){
|
||||||
var atLocation = value.lastIndexOf('@', cursorIndex);
|
var atLocation = value.lastIndexOf('@', cursorIndex);
|
||||||
if(atLocation == -1){return [0,0];}
|
if(atLocation == -1){return [0,0];}
|
||||||
var nextAt = value.indexOf('@', cursorIndex+1);
|
var nextAt = value.indexOf(' @', cursorIndex+1);
|
||||||
|
|
||||||
if(nextAt == -1){nextAt = value.length;}
|
if(nextAt == -1){nextAt = value.length;}
|
||||||
return [atLocation, nextAt];
|
return [atLocation, nextAt];
|
||||||
|
|
@ -115,6 +169,7 @@ var Publisher = {
|
||||||
initialize: function() {
|
initialize: function() {
|
||||||
Publisher.cachedForm = false;
|
Publisher.cachedForm = false;
|
||||||
Publisher.cachedInput = false;
|
Publisher.cachedInput = false;
|
||||||
|
Publisher.cachedHiddenInput = false;
|
||||||
$("div.public_toggle input").live("click", function(evt) {
|
$("div.public_toggle input").live("click", function(evt) {
|
||||||
$("#publisher_service_icons").toggleClass("dim");
|
$("#publisher_service_icons").toggleClass("dim");
|
||||||
if ($(this).attr('checked') == true) {
|
if ($(this).attr('checked') == true) {
|
||||||
|
|
@ -127,9 +182,7 @@ var Publisher = {
|
||||||
};
|
};
|
||||||
|
|
||||||
Publisher.autocompletion.initialize();
|
Publisher.autocompletion.initialize();
|
||||||
Publisher.updateHiddenField();
|
Publisher.hiddenInput().val(Publisher.input().val());
|
||||||
Publisher.form().find('#status_message_fake_message').bind('keydown',
|
|
||||||
Publisher.updateHiddenField);
|
|
||||||
Publisher.form().find("textarea").bind("focus", function(evt) {
|
Publisher.form().find("textarea").bind("focus", function(evt) {
|
||||||
Publisher.open();
|
Publisher.open();
|
||||||
$(this).css('min-height', '42px');
|
$(this).css('min-height', '42px');
|
||||||
|
|
|
||||||
|
|
@ -6,21 +6,6 @@
|
||||||
describe("Publisher", function() {
|
describe("Publisher", function() {
|
||||||
|
|
||||||
describe("initialize", 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(){
|
it("calls close when it does not have text", function(){
|
||||||
spec.loadFixture('aspects_index');
|
spec.loadFixture('aspects_index');
|
||||||
spyOn(Publisher, 'close');
|
spyOn(Publisher, 'close');
|
||||||
|
|
@ -67,17 +52,6 @@ describe("Publisher", function() {
|
||||||
expect(Publisher.form().find(".options_and_submit:visible").length).toBe(0);
|
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(){
|
describe("input", function(){
|
||||||
beforeEach(function(){
|
beforeEach(function(){
|
||||||
spec.loadFixture('aspects_index_prefill');
|
spec.loadFixture('aspects_index_prefill');
|
||||||
|
|
@ -88,6 +62,8 @@ describe("Publisher", function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe("autocompletion", function(){
|
describe("autocompletion", function(){
|
||||||
|
describe("onKeypress", function(){
|
||||||
|
});,
|
||||||
describe("searchTermFromValue", function(){
|
describe("searchTermFromValue", function(){
|
||||||
var func;
|
var func;
|
||||||
beforeEach(function(){func = Publisher.autocompletion.searchTermFromValue;});
|
beforeEach(function(){func = Publisher.autocompletion.searchTermFromValue;});
|
||||||
|
|
@ -124,56 +100,120 @@ describe("Publisher", function() {
|
||||||
describe("onSelect", function(){
|
describe("onSelect", function(){
|
||||||
|
|
||||||
});
|
});
|
||||||
describe("addMentionToHiddenInput", function(){
|
|
||||||
var func;
|
|
||||||
var input;
|
|
||||||
|
|
||||||
|
describe("mentionList", function(){
|
||||||
|
var visibleInput, visibleVal,
|
||||||
|
hiddenInput, hiddenVal,
|
||||||
|
list,
|
||||||
|
func,
|
||||||
|
mention;
|
||||||
beforeEach(function(){
|
beforeEach(function(){
|
||||||
spec.loadFixture('aspects_index');
|
spec.loadFixture('aspects_index');
|
||||||
func = Publisher.autocompletion.addMentionToHiddenInput;
|
list = Publisher.autocompletion.mentionList;
|
||||||
input = Publisher.input();
|
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 func;
|
||||||
var input;
|
var input;
|
||||||
var replaceWith;
|
var replaceWith;
|
||||||
beforeEach(function(){
|
beforeEach(function(){
|
||||||
spec.loadFixture('aspects_index');
|
spec.loadFixture('aspects_index');
|
||||||
func = Publisher.autocompletion.addMentionToVisibleInput;
|
func = Publisher.autocompletion.addMentionToInput;
|
||||||
input = Publisher.input();
|
input = Publisher.input();
|
||||||
replaceWith = "Replace with this.";
|
replaceWith = "Replace with this.";
|
||||||
});
|
});
|
||||||
it("replaces everything after an @ if the cursor is a word after that @", function(){
|
it("replaces everything after an @ if the cursor is a word after that @", function(){
|
||||||
input.val('not @dan grip');
|
input.val('not @dan grip');
|
||||||
func(input, replaceWith);
|
var cursorIndex = 13;
|
||||||
input[0].selectionStart = 13;
|
func(input, cursorIndex, replaceWith);
|
||||||
expect(input.val()).toBe('not ' + replaceWith);
|
expect(input.val()).toBe('not ' + replaceWith);
|
||||||
});
|
});
|
||||||
it("replaces everything after an @ if the cursor is after that @", function(){
|
it("replaces everything after an @ if the cursor is after that @", function(){
|
||||||
input.val('not @dan grip');
|
input.val('not @dan grip');
|
||||||
input[0].selectionStart = 7;
|
var cursorIndex = 7;
|
||||||
func(input, replaceWith);
|
func(input, cursorIndex, replaceWith);
|
||||||
expect(input.val()).toBe('not ' + replaceWith);
|
expect(input.val()).toBe('not ' + replaceWith);
|
||||||
});
|
});
|
||||||
it("replaces everything after an @ at the start of the line", function(){
|
it("replaces everything after an @ at the start of the line", function(){
|
||||||
input.val('@dan grip');
|
input.val('@dan grip');
|
||||||
input[0].selectionStart = 9;
|
var cursorIndex = 9;
|
||||||
func(input, replaceWith);
|
func(input, cursorIndex, replaceWith);
|
||||||
expect(input.val()).toBe(replaceWith);
|
expect(input.val()).toBe(replaceWith);
|
||||||
});
|
});
|
||||||
it("replaces everything between @s if there are 2 @s and the cursor is between them", function(){
|
it("replaces everything between @s if there are 2 @s and the cursor is between them", function(){
|
||||||
input.val('@asdpo aoisdj @asodk');
|
input.val('@asdpo aoisdj @asodk');
|
||||||
input[0].selectionStart = 8;
|
var cursorIndex = 8;
|
||||||
func(input, replaceWith);
|
func(input, cursorIndex, replaceWith);
|
||||||
expect(input.val()).toBe(replaceWith + ' @asodk');
|
expect(input.val()).toBe(replaceWith + ' @asodk');
|
||||||
});
|
});
|
||||||
it("replaces everything after the 2nd @ if there are 2 @s and the cursor after them", function(){
|
it("replaces everything after the 2nd @ if there are 2 @s and the cursor after them", function(){
|
||||||
input.val('@asod asdo @asd asok');
|
input.val('@asod asdo @asd asok');
|
||||||
input[0].selectionStart = 15;
|
var cursorIndex = 15;
|
||||||
func(input, replaceWith);
|
func(input, cursorIndex, replaceWith);
|
||||||
expect(input.val()).toBe('@asod asdo ' + replaceWith);
|
expect(input.val()).toBe('@asod asdo ' + replaceWith);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue