diff --git a/app/assets/javascripts/home.js b/app/assets/javascripts/home.js
index b8335550b..7eb2db874 100644
--- a/app/assets/javascripts/home.js
+++ b/app/assets/javascripts/home.js
@@ -3,6 +3,6 @@
* the COPYRIGHT file.
*/
//= require publisher
-//= require jquery.textchange.min
+//= require jquery.textchange
//= require aspect-edit-pane
//= require fileuploader-custom
\ No newline at end of file
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/app/views/status_messages/new.html.haml b/app/views/status_messages/new.html.haml
index 1ddeb6089..53185ce11 100644
--- a/app/views/status_messages/new.html.haml
+++ b/app/views/status_messages/new.html.haml
@@ -1,7 +1,7 @@
-# Copyright (c) 2010-2011, Diaspora Inc. This file is
-# licensed under the Affero General Public License version 3 or later. See
-# the COPYRIGHT file.
-= javascript_include_tag 'jquery.textchange.min.js', "publisher.js"
+= javascript_include_tag 'jquery.textchange.js', "publisher.js"
:javascript
$(function() {
diff --git a/config/application.rb b/config/application.rb
index cf703cbf0..0101be8b4 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -79,7 +79,7 @@ module Diaspora
# Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)
# Javascripts
config.assets.precompile += [ "aspect-contacts.js", "contact-list.js", "finder.js",
- "home.js", "ie.js", "inbox.js", "jquery.js", "jquery_ujs.js", "jquery.textchange.min.js",
+ "home.js", "ie.js", "inbox.js", "jquery.js", "jquery_ujs.js", "jquery.textchange.js",
"login.js", "mailchimp.js", "main.js", "mobile.js", "profile.js", "people.js", "photos.js",
"profile.js", "publisher.js", "templates.js", "validation.js" ]
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 = $("").appendTo(elmAutocompleteList).hide();
_.each(results, function (item, index) {
+ var itemUid = _.uniqueId('mention_');
+
+ autocompleteItemCollection[itemUid] = _.extend({}, item, {value: item.name});
+
var elmListItem = $(settings.templates.autocompleteListItem({
'id' : utils.htmlEncode(item.id),
'display' : utils.htmlEncode(item.name),
'type' : utils.htmlEncode(item.type),
'content' : utils.highlightTerm(utils.htmlEncode((item.name)), query)
- })).data('mention', item);
+ })).attr('data-uid', itemUid);
- if (index === 0) {
- selectAutoCompleteItem(elmListItem);
+ if (index === 0) {
+ selectAutoCompleteItem(elmListItem);
}
if (settings.showAvatars) {
@@ -323,18 +350,27 @@
}
}
+ function resetInput() {
+ elmInputBox.val('');
+ mentionsCollection = [];
+ updateValues();
+ }
+
// Public methods
return {
- init : function (options) {
- settings = options;
+ init : function (domTarget) {
+
+ domInput = domTarget;
initTextarea();
initAutocomplete();
initMentionsOverlay();
+ resetInput();
- if(options.prefillMention) {
- addMention(options.prefillMention);
+ if( settings.prefillMention ) {
+ addMention( settings.prefillMention );
}
+
},
val : function (callback) {
@@ -347,9 +383,7 @@
},
reset : function () {
- elmInputBox.val('');
- mentionsCollection = [];
- updateNames();
+ resetInput();
},
getMentions : function (callback) {
@@ -364,20 +398,20 @@
$.fn.mentionsInput = function (method, settings) {
- if (typeof method === 'object' || !method) {
- settings = $.extend(true, {}, defaultSettings, method);
- }
-
var outerArguments = arguments;
+ if (typeof method === 'object' || !method) {
+ settings = method;
+ }
+
return this.each(function () {
- var instance = $.data(this, 'mentionsInput') || $.data(this, 'mentionsInput', new MentionsInput(this));
+ var instance = $.data(this, 'mentionsInput') || $.data(this, 'mentionsInput', new MentionsInput(settings));
if (_.isFunction(instance[method])) {
return instance[method].apply(this, Array.prototype.slice.call(outerArguments, 1));
} else if (typeof method === 'object' || !method) {
- return instance.init.call(this, settings);
+ return instance.init.call(this, this);
} else {
$.error('Method ' + method + ' does not exist');
diff --git a/vendor/assets/javascripts/jquery.textchange.js b/vendor/assets/javascripts/jquery.textchange.js
new file mode 100644
index 000000000..3affea18e
--- /dev/null
+++ b/vendor/assets/javascripts/jquery.textchange.js
@@ -0,0 +1,76 @@
+/*!
+ * jQuery TextChange Plugin
+ * http://www.zurb.com/playground/jquery-text-change-custom-event
+ *
+ * Copyright 2010, ZURB
+ * Released under the MIT License
+ */
+(function ($) {
+
+ $.event.special.textchange = {
+
+ setup: function (data, namespaces) {
+ $(this).data('lastValue', this.contentEditable === 'true' ? $(this).html() : $(this).val());
+ $(this).bind('keyup.textchange', $.event.special.textchange.handler);
+ $(this).bind('cut.textchange paste.textchange input.textchange', $.event.special.textchange.delayedHandler);
+ },
+
+ teardown: function (namespaces) {
+ $(this).unbind('.textchange');
+ },
+
+ handler: function (event) {
+ $.event.special.textchange.triggerIfChanged($(this));
+ },
+
+ delayedHandler: function (event) {
+ var element = $(this);
+ setTimeout(function () {
+ $.event.special.textchange.triggerIfChanged(element);
+ }, 25);
+ },
+
+ triggerIfChanged: function (element) {
+ var current = element[0].contentEditable === 'true' ? element.html() : element.val();
+ if (current !== element.data('lastValue')) {
+ element.trigger('textchange', [element.data('lastValue')]);
+ element.data('lastValue', current);
+ }
+ }
+ };
+
+ $.event.special.hastext = {
+
+ setup: function (data, namespaces) {
+ $(this).bind('textchange', $.event.special.hastext.handler);
+ },
+
+ teardown: function (namespaces) {
+ $(this).unbind('textchange', $.event.special.hastext.handler);
+ },
+
+ handler: function (event, lastValue) {
+ if ((lastValue === '') && lastValue !== $(this).val()) {
+ $(this).trigger('hastext');
+ }
+ }
+ };
+
+ $.event.special.notext = {
+
+ setup: function (data, namespaces) {
+ $(this).bind('textchange', $.event.special.notext.handler);
+ },
+
+ teardown: function (namespaces) {
+ $(this).unbind('textchange', $.event.special.notext.handler);
+ },
+
+ handler: function (event, lastValue) {
+ if ($(this).val() === '' && $(this).val() !== lastValue) {
+ $(this).trigger('notext');
+ }
+ }
+ };
+
+})(jQuery);
\ No newline at end of file
diff --git a/vendor/assets/javascripts/jquery.textchange.min.js b/vendor/assets/javascripts/jquery.textchange.min.js
deleted file mode 100644
index 93f179a23..000000000
--- a/vendor/assets/javascripts/jquery.textchange.min.js
+++ /dev/null
@@ -1,10 +0,0 @@
-/*!
- * jQuery TextChange Plugin
- * http://www.zurb.com/playground/jquery-text-change-custom-event
- *
- * Copyright 2010, ZURB
- * Released under the MIT License
- */
- (function(a){a.event.special.textchange={setup:function(){a(this).data("lastValue",this.contentEditable==="true"?a(this).html():a(this).val());a(this).bind("keyup.textchange",a.event.special.textchange.handler);a(this).bind("cut.textchange paste.textchange input.textchange",a.event.special.textchange.delayedHandler)},teardown:function(){a(this).unbind(".textchange")},handler:function(){a.event.special.textchange.triggerIfChanged(a(this))},delayedHandler:function(){var c=a(this);setTimeout(function(){a.event.special.textchange.triggerIfChanged(c)},
- 25)},triggerIfChanged:function(a){var b=a[0].contentEditable==="true"?a.html():a.val();b!==a.data("lastValue")&&(a.trigger("textchange",[a.data("lastValue")]),a.data("lastValue",b))}};a.event.special.hastext={setup:function(){a(this).bind("textchange",a.event.special.hastext.handler)},teardown:function(){a(this).unbind("textchange",a.event.special.hastext.handler)},handler:function(c,b){b===""&&b!==a(this).val()&&a(this).trigger("hastext")}};a.event.special.notext={setup:function(){a(this).bind("textchange",
- a.event.special.notext.handler)},teardown:function(){a(this).unbind("textchange",a.event.special.notext.handler)},handler:function(c,b){a(this).val()===""&&a(this).val()!==b&&a(this).trigger("notext")}}})(jQuery);
\ No newline at end of file