diff --git a/app/assets/javascripts/app/views/publisher/mention_view.js b/app/assets/javascripts/app/views/publisher/mention_view.js
new file mode 100644
index 0000000000000000000000000000000000000000..270e14161bf091d10c52b6fe20ae9e93bc6f7b6e
--- /dev/null
+++ b/app/assets/javascripts/app/views/publisher/mention_view.js
@@ -0,0 +1,322 @@
+//= require ../search_base_view
+
+/*
+ * This file is based on jQuery.mentionsInput by Kenneth Auchenberg
+ * licensed under MIT License - http://www.opensource.org/licenses/mit-license.php
+ * Website: https://podio.github.io/jquery-mentions-input/
+ */
+
+app.views.PublisherMention = app.views.SearchBase.extend({
+  KEYS: {
+    BACKSPACE: 8, TAB: 9, RETURN: 13, ESC: 27, LEFT: 37, UP: 38,
+    RIGHT: 39, DOWN: 40, COMMA: 188, SPACE: 32, HOME: 36, END: 35
+  },
+
+  settings: {
+    triggerChar: "@",
+    minChars: 2,
+    templates: {
+      wrapper: _.template("<div class='mentions-input-box'></div>"),
+      mentionsOverlay: _.template("<div class='mentions-box'><div class='mentions'><div></div></div></div>"),
+      mentionItemSyntax: _.template("@{<%= name %> ; <%= handle %>}"),
+      mentionItemHighlight: _.template("<strong><span><%= name %></span></strong>")
+    }
+  },
+
+  utils: {
+    setCaretPosition: function(domNode, caretPos){
+      if(domNode.createTextRange){
+        var range = domNode.createTextRange();
+        range.move("character", caretPos);
+        range.select();
+      } else{
+        if(domNode.selectionStart){
+          domNode.focus();
+          domNode.setSelectionRange(caretPos, caretPos);
+        } else{
+          domNode.focus();
+        }
+      }
+    },
+
+    rtrim: function(string){
+      return string.replace(/\s+$/, "");
+    }
+  },
+
+  events: {
+    "keydown #status_message_fake_text": "onInputBoxKeyDown",
+    "keypress #status_message_fake_text": "onInputBoxKeyPress",
+    "input #status_message_fake_text": "onInputBoxInput",
+    "click #status_message_fake_text": "onInputBoxClick",
+    "blur #status_message_fake_text": "onInputBoxBlur"
+  },
+
+  initialize: function(){
+    this.mentionsCollection = [];
+    this.inputBuffer = [];
+    this.currentDataQuery = "";
+    this.mentionChar = "\u200B";
+
+    this.elmInputBox = this.$el.find("#status_message_fake_text");
+    this.elmInputWrapper = this.elmInputBox.parent();
+    this.elmWrapperBox = $(this.settings.templates.wrapper());
+    this.elmInputBox.wrapAll(this.elmWrapperBox);
+    this.elmWrapperBox = this.elmInputWrapper.find("> div").first();
+    this.elmMentionsOverlay = $(this.settings.templates.mentionsOverlay());
+    this.elmMentionsOverlay.prependTo(this.elmWrapperBox);
+
+    var self = this;
+    this.getSearchInput().on("typeahead:select", function(evt, datum){
+      self.processMention(datum);
+      self.resetMentionBox();
+      self.addToFilteredResults(datum);
+    });
+
+    this.getSearchInput().on("typeahead:render", function(){
+      self.select(self.$(".tt-menu .tt-suggestion").first());
+    });
+
+    this.completeSetup(this.getSearchInput());
+
+    this.$el.find(".twitter-typeahead").css({position: "absolute", left: "-1px"});
+    this.$el.find(".twitter-typeahead .tt-menu").css("margin-top", 0);
+  },
+
+  clearBuffer: function(){
+    this.inputBuffer.length = 0;
+  },
+
+  updateMentionsCollection: function(){
+    var inputText = this.getInputBoxValue();
+
+    this.mentionsCollection = _.reject(this.mentionsCollection, function(mention){
+      return !mention.name || inputText.indexOf(mention.name) === -1;
+    });
+    this.mentionsCollection = _.compact(this.mentionsCollection);
+  },
+
+  addMention: function(person){
+    if(!person || !person.name || !person.handle){
+      return;
+    }
+    // This is needed for processing preview
+    /* jshint camelcase: false */
+    person.diaspora_id = person.handle;
+    /* jshint camelcase: true */
+    this.mentionsCollection.push(person);
+  },
+
+  processMention: function(mention){
+    var currentMessage = this.getInputBoxValue();
+
+    // Using a regex to figure out positions
+    var regex = new RegExp("\\" + this.settings.triggerChar + this.currentDataQuery, "gi");
+    regex.exec(currentMessage);
+
+    var startCaretPosition = regex.lastIndex - this.currentDataQuery.length - 1;
+    var currentCaretPosition = regex.lastIndex;
+
+    var start = currentMessage.substr(0, startCaretPosition);
+    var end = currentMessage.substr(currentCaretPosition, currentMessage.length);
+    var startEndIndex = (start + mention.name).length + 1;
+
+    this.addMention(mention);
+
+    // Cleaning before inserting the value, otherwise auto-complete would be triggered with "old" inputbuffer
+    this.clearBuffer();
+    this.currentDataQuery = "";
+    this.resetMentionBox();
+
+    // Mentions & syntax message
+    var updatedMessageText = start + this.mentionChar + mention.name + end;
+    this.elmInputBox.val(updatedMessageText);
+    this.updateValues();
+
+    // Set correct focus and selection
+    this.elmInputBox.focus();
+    this.utils.setCaretPosition(this.elmInputBox[0], startEndIndex);
+  },
+
+  updateValues: function(){
+    var syntaxMessage = this.getInputBoxValue();
+    var mentionText = this.getInputBoxValue();
+    this.clearFilteredResults();
+
+    var self = this;
+
+    _.each(this.mentionsCollection, function(mention){
+      self.addToFilteredResults(mention);
+
+      var mentionVal = self.mentionChar + mention.name;
+
+      var textSyntax = self.settings.templates.mentionItemSyntax(mention);
+      syntaxMessage = syntaxMessage.replace(mentionVal, textSyntax);
+
+      var textHighlight = self.settings.templates.mentionItemHighlight({ name: _.escape(mention.name) });
+      mentionText = mentionText.replace(mentionVal, textHighlight);
+    });
+
+    mentionText = mentionText.replace(/\n/g, "<br/>");
+    mentionText = mentionText.replace(/ {2}/g, "&nbsp; ");
+
+    this.elmInputBox.data("messageText", syntaxMessage);
+    this.elmMentionsOverlay.find("div > div").html(mentionText);
+  },
+
+  /**
+   * Let us prefill the publisher with a mention list
+   * @param persons List of people to mention in a post;
+   * JSON object of form { handle: <diaspora handle>, name: <name>, ... }
+   */
+  prefillMention: function(persons){
+    var self = this;
+    _.each(persons, function(person){
+      self.addMention(person);
+      self.addToFilteredResults(person);
+      self.elmInputBox.val(self.mentionChar + person.name);
+      self.updateValues();
+    });
+  },
+
+  selectNextResult: function(evt){
+    if(this.isVisible()){
+      evt.preventDefault();
+      evt.stopPropagation();
+    }
+
+    if(this.getSelected().size() !== 1 || this.getSelected().next().size() !== 1){
+      this.getSelected().removeClass("tt-cursor");
+      this.$el.find(".tt-suggestion").first().addClass("tt-cursor");
+    }
+    else{
+      this.getSelected().removeClass("tt-cursor").next().addClass("tt-cursor");
+    }
+  },
+
+  selectPreviousResult: function(evt){
+    if(this.isVisible()){
+      evt.preventDefault();
+      evt.stopPropagation();
+    }
+
+    if(this.getSelected().size() !== 1 || this.getSelected().prev().size() !== 1){
+      this.getSelected().removeClass("tt-cursor");
+      this.$el.find(".tt-suggestion").last().addClass("tt-cursor");
+    }
+    else{
+      this.getSelected().removeClass("tt-cursor").prev().addClass("tt-cursor");
+    }
+  },
+
+  onInputBoxKeyPress: function(e){
+    if(e.keyCode !== this.KEYS.BACKSPACE){
+      var typedValue = String.fromCharCode(e.which || e.keyCode);
+      this.inputBuffer.push(typedValue);
+    }
+  },
+
+  onInputBoxInput: function(){
+    this.updateValues();
+    this.updateMentionsCollection();
+
+    var triggerCharIndex = _.lastIndexOf(this.inputBuffer, this.settings.triggerChar);
+    if(triggerCharIndex > -1){
+      this.currentDataQuery = this.inputBuffer.slice(triggerCharIndex + 1).join("");
+      this.currentDataQuery = this.utils.rtrim(this.currentDataQuery);
+
+      this.showMentionBox();
+    }
+  },
+
+  onInputBoxKeyDown: function(e){
+    // This also matches HOME/END on OSX which is CMD+LEFT, CMD+RIGHT
+    if(e.keyCode === this.KEYS.LEFT || e.keyCode === this.KEYS.RIGHT ||
+        e.keyCode === this.KEYS.HOME || e.keyCode === this.KEYS.END){
+      _.defer(this.clearBuffer);
+
+      // 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(this.updateValues);
+      }
+
+      return;
+    }
+
+    if(e.keyCode === this.KEYS.BACKSPACE){
+      this.inputBuffer = this.inputBuffer.slice(0, this.inputBuffer.length - 1);
+      return;
+    }
+
+    if(!this.isVisible){
+      return true;
+    }
+
+    switch(e.keyCode){
+      case this.KEYS.ESC:
+      case this.KEYS.SPACE:
+        this.resetMentionBox();
+        break;
+      case this.KEYS.UP:
+        this.selectPreviousResult(e);
+        break;
+      case this.KEYS.DOWN:
+        this.selectNextResult(e);
+        break;
+      case this.KEYS.RETURN:
+      case this.KEYS.TAB:
+        if(this.getSelected().size() === 1){
+          this.getSelected().click();
+          return false;
+        }
+        break;
+    }
+    return true;
+  },
+
+  onInputBoxClick: function(){
+    this.resetMentionBox();
+  },
+
+  onInputBoxBlur: function(){
+    this.resetMentionBox();
+  },
+
+  reset: function(){
+    this.elmInputBox.val("");
+    this.mentionsCollection.length = 0;
+    this.clearFilteredResults();
+    this.updateValues();
+  },
+
+  showMentionBox: function(){
+    this.getSearchInput().typeahead("val", this.currentDataQuery);
+    this.getSearchInput().typeahead("open");
+  },
+
+  resetMentionBox: function(){
+    this.getSearchInput().typeahead("val", "");
+    this.getSearchInput().typeahead("close");
+  },
+
+  getInputBoxValue: function(){
+    return $.trim(this.elmInputBox.val());
+  },
+
+  isVisible: function(){
+    return this.$el.find(".tt-menu").is(":visible");
+  },
+
+  getSearchInput: function(){
+    if(this.$el.find(".typeahead-mention-box").length === 0){
+      this.elmInputBox.after("<input class='typeahead-mention-box hidden' type='text'/>");
+    }
+    return this.$el.find(".typeahead-mention-box");
+  },
+
+  getTextForSubmit: function(){
+    return this.mentionsCollection.length ? this.elmInputBox.data("messageText") : this.getInputBoxValue();
+  }
+});
diff --git a/app/assets/javascripts/app/views/publisher_view.js b/app/assets/javascripts/app/views/publisher_view.js
index 89e5e38f5b7f128ad8357f3f825b9ae0905bcc29..fd7d85a5e4853ccd22f0a8a8955d598a77b7cf6d 100644
--- a/app/assets/javascripts/app/views/publisher_view.js
+++ b/app/assets/javascripts/app/views/publisher_view.js
@@ -31,6 +31,7 @@ app.views.Publisher = Backbone.View.extend({
 
   initialize : function(opts){
     this.standalone = opts ? opts.standalone : false;
+    this.prefillMention = opts && opts.prefillMention ? opts.prefillMention : undefined;
     this.disabled   = false;
 
     // init shortcut references to the various elements
@@ -41,9 +42,6 @@ app.views.Publisher = Backbone.View.extend({
     this.previewEl = this.$("button.post_preview_button");
     this.photozoneEl = this.$("#photodropzone");
 
-    // init mentions plugin
-    Mentions.initialize(this.inputEl);
-
     // if there is data in the publisher we ask for a confirmation
     // before the user is able to leave the page
     $(window).on("beforeunload", _.bind(this._beforeUnload, this));
@@ -100,6 +98,11 @@ app.views.Publisher = Backbone.View.extend({
   },
 
   initSubviews: function() {
+    this.mention = new app.views.PublisherMention({ el: this.$("#publisher_textarea_wrapper") });
+    if(this.prefillMention){
+      this.mention.prefillMention([this.prefillMention]);
+    }
+
     var form = this.$(".content_creation form");
 
     this.view_services = new app.views.PublisherServices({
@@ -265,32 +268,6 @@ app.views.Publisher = Backbone.View.extend({
     return photos;
   },
 
-  getMentionedPeople: function(serializedForm) {
-    var mentionedPeople = [],
-        regexp = /@{([^;]+); ([^}]+)}/g,
-        user;
-    var getMentionedUser = function(handle) {
-      return Mentions.contacts.filter(function(user) {
-        return user.handle === handle;
-      })[0];
-    };
-
-    while( (user = regexp.exec(serializedForm["status_message[text]"])) ) {
-      // user[1]: name, user[2]: handle
-      var mentionedUser = getMentionedUser(user[2]);
-      if(mentionedUser){
-        mentionedPeople.push({
-          "id": mentionedUser.id,
-          "guid": mentionedUser.guid,
-          "name": user[1],
-          "diaspora_id": user[2],
-          "avatar": mentionedUser.avatar
-        });
-      }
-    }
-    return mentionedPeople;
-  },
-
   getPollData: function(serializedForm) {
     var poll;
     var pollQuestion = serializedForm.poll_question;
@@ -321,7 +298,7 @@ app.views.Publisher = Backbone.View.extend({
 
     var serializedForm = $(evt.target).closest("form").serializeObject();
     var photos = this.getUploadedPhotos();
-    var mentionedPeople = this.getMentionedPeople(serializedForm);
+    var mentionedPeople = this.mention.mentionsCollection;
     var date = (new Date()).toISOString();
     var poll = this.getPollData(serializedForm);
     var locationCoords = serializedForm["location[coords]"];
@@ -395,7 +372,7 @@ app.views.Publisher = Backbone.View.extend({
     autosize.update(this.inputEl);
 
     // remove mentions
-    this.inputEl.mentionsInput("reset");
+    this.mention.reset();
 
     // remove photos
     this.photozoneEl.find("li").remove();
@@ -450,9 +427,6 @@ app.views.Publisher = Backbone.View.extend({
     this.$el.removeClass("closed");
     this.wrapperEl.addClass("active");
     autosize.update(this.inputEl);
-
-    // fetch contacts for mentioning
-    Mentions.fetchContacts();
     return this;
   },
 
@@ -521,9 +495,7 @@ app.views.Publisher = Backbone.View.extend({
     var self = this;
 
     this.checkSubmitAvailability();
-    this.inputEl.mentionsInput("val", function(value){
-      self.hiddenInputEl.val(value);
-    });
+    this.hiddenInputEl.val(this.mention.getTextForSubmit());
   },
 
   _beforeUnload: function(e) {
diff --git a/app/assets/javascripts/app/views/search_base_view.js b/app/assets/javascripts/app/views/search_base_view.js
new file mode 100644
index 0000000000000000000000000000000000000000..af791412291f8b50cb957fb860fc50b5554d7454
--- /dev/null
+++ b/app/assets/javascripts/app/views/search_base_view.js
@@ -0,0 +1,140 @@
+app.views.SearchBase = app.views.Base.extend({
+  completeSetup: function(typeaheadElement){
+    this.typeaheadElement = $(typeaheadElement);
+    this.setupBloodhound();
+    this.setupTypeahead();
+    this.bindSelectionEvents();
+    this.resultsTofilter = [];
+  },
+
+  setupBloodhound: function() {
+    var self = this;
+    var bloodhoundConf = {
+      datumTokenizer: function(datum) {
+        var nameTokens = Bloodhound.tokenizers.nonword(datum.name);
+        var handleTokens = datum.handle ? Bloodhound.tokenizers.nonword(datum.name) : [];
+        return nameTokens.concat(handleTokens);
+      },
+      queryTokenizer: Bloodhound.tokenizers.whitespace,
+      prefetch: {
+        url: "/contacts.json",
+        transform: this.transformBloodhoundResponse,
+        cache: false
+      },
+      sufficient: 5
+    };
+
+    // The publisher does not define an additionnal source for searchin
+    // This prevents tests from failing when this additionnal source isn't set
+    if(this.searchFormAction !== undefined){
+      bloodhoundConf.remote = {
+        url: this.searchFormAction + ".json?q=%QUERY",
+        wildcard: "%QUERY",
+        transform: this.transformBloodhoundResponse
+      };
+    }
+
+    this.bloodhound = new Bloodhound(bloodhoundConf);
+
+    /**
+     * Custom searching function that let us filter contacts from prefetched Bloodhound results.
+     */
+    this.bloodhound.customSearch = function(query, sync, async){
+      var filterResults = function(datums){
+        return _.filter(datums, function(result){
+          if(result.handle){
+            return !_.contains(self.resultsTofilter, result.handle);
+          }
+        });
+      };
+
+      var _sync = function(datums){
+        var results = filterResults(datums);
+        sync(results);
+      };
+
+      self.bloodhound.search(query, _sync, async);
+    };
+  },
+
+  setupTypeahead: function() {
+    this.typeaheadElement.typeahead({
+          hint: false,
+          highlight: true,
+          minLength: 2
+        },
+        {
+          name: "search",
+          display: "name",
+          limit: 5,
+          source: this.bloodhound.customSearch,
+          templates: {
+            /* jshint camelcase: false */
+            suggestion: HandlebarsTemplates.search_suggestion_tpl
+            /* jshint camelcase: true */
+          }
+        });
+  },
+
+  transformBloodhoundResponse: function(response) {
+    return response.map(function(data){
+      // person
+      if(data.handle){
+        data.person = true;
+        return data;
+      }
+
+      // hashtag
+      return {
+        hashtag: true,
+        name: data.name,
+        url: Routes.tag(data.name.substring(1))
+      };
+    });
+  },
+
+  /**
+   * This bind events to highlight a result when overing it
+   */
+  bindSelectionEvents: function(){
+    var self = this;
+    var onover = function(evt){
+      var isSuggestion = $(evt.target).is(".tt-suggestion");
+      var suggestion = isSuggestion ? $(evt.target) : $(evt.target).parent(".tt-suggestion");
+      if(suggestion){
+        self.select(suggestion);
+      }
+    };
+
+    this.typeaheadElement.on("typeahead:render", function(){
+      self.$(".tt-menu *").off("mouseover", onover);
+      self.$(".tt-menu .tt-suggestion").on("mouseover", onover);
+      self.$(".tt-menu .tt-suggestion *").on("mouseover", onover);
+    });
+  },
+
+  /**
+   * This function lets us filter contacts from Bloodhound's responses
+   * It is used by app.views.PublisherMention to filter already mentionned
+   * people in post. Does not filter tags from results.
+   * @param person a JSON object of form { handle: <diaspora handle>, ... } representing the filtered contact
+   */
+  addToFilteredResults: function(person){
+    if(person.handle){
+      this.resultsTofilter.push(person.handle);
+    }
+  },
+
+  clearFilteredResults: function(){
+    this.resultsTofilter.length = 0;
+  },
+
+  getSelected: function(){
+    return this.$el.find(".tt-cursor");
+  },
+
+  select: function(el){
+    this.getSelected().removeClass("tt-cursor");
+    $(el).addClass("tt-cursor");
+  }
+});
diff --git a/app/assets/javascripts/app/views/search_view.js b/app/assets/javascripts/app/views/search_view.js
index 6e8a1d272e79c6dccdae31c1af1290b54e39dcb7..65ed775c0dc9a28ec3fca78aaee4a6815c5b6c35 100644
--- a/app/assets/javascripts/app/views/search_view.js
+++ b/app/assets/javascripts/app/views/search_view.js
@@ -1,96 +1,52 @@
 // @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
-app.views.Search = app.views.Base.extend({
+app.views.Search = app.views.SearchBase.extend({
   events: {
     "focusin #q": "toggleSearchActive",
     "focusout #q": "toggleSearchActive",
-    "keypress #q": "inputKeypress",
+    "keypress #q": "inputKeypress"
   },
 
   initialize: function(){
     this.searchFormAction = this.$el.attr("action");
-    this.searchInput = this.$("#q");
-
-    // constructs the suggestion engine
-    this.setupBloodhound();
-    this.setupTypeahead();
-    this.searchInput.on("typeahead:select", this.suggestionSelected);
-  },
-
-  setupBloodhound: function() {
-    this.bloodhound = new Bloodhound({
-      datumTokenizer: function(datum) {
-        var nameTokens = Bloodhound.tokenizers.nonword(datum.name);
-        var handleTokens = datum.handle ? Bloodhound.tokenizers.nonword(datum.name) : [];
-        return nameTokens.concat(handleTokens);
-      },
-      queryTokenizer: Bloodhound.tokenizers.whitespace,
-      remote: {
-        url: this.searchFormAction + ".json?q=%QUERY",
-        wildcard: "%QUERY",
-        transform: this.transformBloodhoundResponse
-      },
-      prefetch: {
-        url: "/contacts.json",
-        transform: this.transformBloodhoundResponse,
-        cache: false
-      },
-      sufficient: 5
-    });
+    this.completeSetup(this.getTypeaheadElement());
+    this.bindMoreSelectionEvents();
+    this.getTypeaheadElement().on("typeahead:select", this.suggestionSelected);
   },
 
-  setupTypeahead: function() {
-    this.searchInput.typeahead({
-      hint: false,
-      highlight: true,
-      minLength: 2
-    },
-    {
-      name: "search",
-      display: "name",
-      limit: 5,
-      source: this.bloodhound,
-      templates: {
-        /* jshint camelcase: false */
-        suggestion: HandlebarsTemplates.search_suggestion_tpl
-        /* jshint camelcase: true */
-      }
+  /**
+   * This bind events to unselect all results when leaving the menu
+   */
+  bindMoreSelectionEvents: function(){
+    var self = this;
+    var onleave = function(){
+      self.$(".tt-cursor").removeClass("tt-cursor");
+    };
+
+    this.getTypeaheadElement().on("typeahead:render", function(){
+      self.$(".tt-menu").off("mouseleave", onleave);
+      self.$(".tt-menu").on("mouseleave", onleave);
     });
   },
 
-  transformBloodhoundResponse: function(response) {
-    var result = response.map(function(data) {
-      // person
-      if(data.handle) {
-        data.person = true;
-        return data;
-      }
-
-      // hashtag
-      return {
-        hashtag: true,
-        name: data.name,
-        url: Routes.tag(data.name.substring(1))
-      };
-    });
-
-    return result;
+  getTypeaheadElement: function(){
+    return this.$("#q");
   },
 
-  toggleSearchActive: function(evt) {
+  toggleSearchActive: function(evt){
     // jQuery produces two events for focus/blur (for bubbling)
     // don't rely on which event arrives first, by allowing for both variants
     var isActive = (_.indexOf(["focus","focusin"], evt.type) !== -1);
     $(evt.target).toggleClass("active", isActive);
   },
 
-  suggestionSelected: function(evt, datum) {
-    window.location = datum.url;
-  },
-
-  inputKeypress: function(evt) {
-    if(evt.which === 13 && $(".tt-suggestion.tt-cursor").length === 0) {
+  inputKeypress: function(evt){
+    if(evt.which === 13 && $(".tt-suggestion.tt-cursor").length === 0){
       $(evt.target).closest("form").submit();
     }
+  },
+
+  suggestionSelected: function(evt, datum){
+    window.location = datum.url;
   }
 });
 // @license-ends
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index 78da464960810ee67c9baa115b8b8f992a50f469..458824bd1e7fe797c56fe284d3c05fb3501309fb 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -14,7 +14,6 @@
 //= require rails-timeago
 //= require jquery.events.input
 //= require jakobmattsson-jquery-elastic
-//= require jquery.mentionsInput
 //= require jquery.infinitescroll-custom
 //= require jquery-ui/core
 //= require jquery-ui/widget
diff --git a/app/assets/stylesheets/publisher.scss b/app/assets/stylesheets/publisher.scss
index 32e1e49e3e7811bb8226e7e9565d129460d7af47..9d6b2cd3ff4bbeb38bb67b7aef82ef4668e54946 100644
--- a/app/assets/stylesheets/publisher.scss
+++ b/app/assets/stylesheets/publisher.scss
@@ -83,7 +83,7 @@
       }
 
       &.active textarea {
-        min-height: 70px;
+        min-height: 90px;
       }
 
       .markdownIndications {
@@ -118,6 +118,8 @@
         }
       }
 
+      &:not(.with_location) #location_container { display: none; }
+
       &.with_location .loader {
         height: 20px;
         width: 20px;
diff --git a/app/assets/stylesheets/typeahead.scss b/app/assets/stylesheets/typeahead.scss
index 3a46e14b621c847b3f5f1d047257140cca76f644..ed4405c01bc56b55a26b84e4347dab8765c92a03 100644
--- a/app/assets/stylesheets/typeahead.scss
+++ b/app/assets/stylesheets/typeahead.scss
@@ -12,10 +12,9 @@
   line-height: 20px;
   &.tt-cursor {
     background-color: $brand-primary;
+    border-top: 1px solid $brand-primary;
   }
 
-  &:hover { background-color: lighten($navbar-inverse-bg, 10%); }
-
   &.search-suggestion-person {
     padding: 8px;
     .avatar {
diff --git a/app/views/people/contacts.haml b/app/views/people/contacts.haml
index b1e993f790ea14e090f0ef14abb5251ca1e1a8dd..b5edec9b04c6c1bec46a98deda807a6dd0377bda 100644
--- a/app/views/people/contacts.haml
+++ b/app/views/people/contacts.haml
@@ -1,9 +1,3 @@
--# TODO this should happen in the js app
-- content_for :head do
-  - if user_signed_in? && @person != current_user.person
-    :javascript
-      Mentions.options.prefillMention = Mentions._contactToMention(#{j @person.to_json});
-
 - content_for :page_title do
   = @person.name
 
diff --git a/app/views/people/show.html.haml b/app/views/people/show.html.haml
index bfb10872ce25aff935cd4b25ed423c002e6ee73b..0deab6f098fce53dc25307d9dba982be588af5c1 100644
--- a/app/views/people/show.html.haml
+++ b/app/views/people/show.html.haml
@@ -2,12 +2,6 @@
 -#   licensed under the Affero General Public License version 3 or later.  See
 -#   the COPYRIGHT file.
 
--# TODO this should happen in the js app
-- content_for :head do
-  - if user_signed_in? && @person != current_user.person
-    :javascript
-      Mentions.options.prefillMention = Mentions._contactToMention(#{j @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 88e150b9f80c577c3cd175c6a363bb19a56b7109..f1d7f9a8a45f7c57efc4a29592244a113e43d7c1 100644
--- a/app/views/status_messages/new.html.haml
+++ b/app/views/status_messages/new.html.haml
@@ -7,7 +7,8 @@
 :javascript
   $(function() {
     app.publisher = new app.views.Publisher({
-      standalone: true
+      standalone: true,
+      prefillMention: #{json_escape @person.to_json}
     });
     app.publisher.open();
     $("#publisher").bind('ajax:success', function(){
diff --git a/features/step_definitions/mention_steps.rb b/features/step_definitions/mention_steps.rb
index f7939afbe9f604e507624720fe8b70ef1ed86435..a3655756357784e994f427f12a3211f11e9516e8 100644
--- a/features/step_definitions/mention_steps.rb
+++ b/features/step_definitions/mention_steps.rb
@@ -1,24 +1,24 @@
 And /^Alice has a post mentioning Bob$/ do
-  alice = User.find_by_email 'alice@alice.alice'
-  bob = User.find_by_email 'bob@bob.bob'
+  alice = User.find_by_email "alice@alice.alice"
+  bob = User.find_by_email "bob@bob.bob"
   aspect = alice.aspects.where(:name => "Besties").first
   alice.post(:status_message, :text => "@{Bob Jones; #{bob.person.diaspora_handle}}", :to => aspect)
 end
 
 And /^Alice has (\d+) posts mentioning Bob$/ do |n|
   n.to_i.times do
-    alice = User.find_by_email 'alice@alice.alice'
-    bob = User.find_by_email 'bob@bob.bob'
+    alice = User.find_by_email "alice@alice.alice"
+    bob = User.find_by_email "bob@bob.bob"
     aspect = alice.aspects.where(:name => "Besties").first
     alice.post(:status_message, :text => "@{Bob Jones; #{bob.person.diaspora_handle}}", :to => aspect)
   end
 end
 
 And /^I mention Alice in the publisher$/ do
-  alice = User.find_by_email 'alice@alice.alice'
-  write_in_publisher("@{Alice Smith ; #{alice.person.diaspora_handle}}")
+  write_in_publisher("@alice")
+  step %(I click on the first user in the mentions dropdown list)
 end
 
 And /^I click on the first user in the mentions dropdown list$/ do
-  find('.mentions-autocomplete-list li', match: :first).click
+  find(".tt-menu .tt-suggestion", match: :first).click
 end
diff --git a/lib/assets/javascripts/jquery.mentionsInput.js b/lib/assets/javascripts/jquery.mentionsInput.js
deleted file mode 100644
index 7693caaeef872f2ee1ff7a4e5ca224fc27c3b9dd..0000000000000000000000000000000000000000
--- a/lib/assets/javascripts/jquery.mentionsInput.js
+++ /dev/null
@@ -1,443 +0,0 @@
-// @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt Expat
-/*
- * Mentions Input
- * Version 1.0.2
- * Written by: Kenneth Auchenberg (Podio)
- *
- * Using underscore.js
- *
- * License: MIT License - http://www.opensource.org/licenses/mit-license.php
- *
- * Modifications for Diaspora:
- *
- * Prevent replacing the wrong text by marking the replacement position with a special character
- * Don't add a space after inserting a mention
- * Only use the first div as a wrapperBox
- * Binded paste event on input box to trigger contacts search for autocompletion while adding mention via clipboard
- */
-
-(function ($, _, undefined) {
-
-  // Settings
-  var KEY = { PASTE : 118, BACKSPACE : 8, TAB : 9, RETURN : 13, ESC : 27, LEFT : 37, UP : 38, RIGHT : 39,
-              DOWN : 40, COMMA : 188, SPACE : 32, HOME : 36, END : 35 }; // Keys "enum"
-  var defaultSettings = {
-    triggerChar   : '@',
-    onDataRequest : $.noop,
-    minChars      : 2,
-    showAvatars   : true,
-    elastic       : true,
-    classes       : {
-      autoCompleteItemActive : "active"
-    },
-    templates     : {
-      wrapper                    : _.template('<div class="mentions-input-box"></div>'),
-      autocompleteList           : _.template('<div class="mentions-autocomplete-list"></div>'),
-      autocompleteListItem       : _.template('<li data-ref-id="<%= id %>" data-ref-type="<%= type %>" data-display="<%= display %>"><%= content %></li>'),
-      autocompleteListItemAvatar : _.template('<img  src="<%= avatar %>" />'),
-      autocompleteListItemIcon   : _.template('<div class="icon <%= icon %>"></div>'),
-      mentionsOverlay            : _.template('<div class="mentions-box"><div class="mentions"><div></div></div></div>'),
-      mentionItemSyntax          : _.template('@[<%= value %>](<%= type %>:<%= id %>)'),
-      mentionItemHighlight       : _.template('<strong><span><%= value %></span></strong>')
-    }
-  };
-
-  var utils = {
-    htmlEncode       : function (str) {
-      return _.escape(str);
-    },
-    highlightTerm    : function (value, term) {
-      if (!term && !term.length) {
-        return value;
-      }
-      return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<b>$1</b>");
-    },
-    setCaratPosition : function (domNode, caretPos) {
-      if (domNode.createTextRange) {
-        var range = domNode.createTextRange();
-        range.move('character', caretPos);
-        range.select();
-      } else {
-        if (domNode.selectionStart) {
-          domNode.focus();
-          domNode.setSelectionRange(caretPos, caretPos);
-        } else {
-          domNode.focus();
-        }
-      }
-    },
-    rtrim: function(string) {
-      return string.replace(/\s+$/,"");
-    }
-  };
-
-  var MentionsInput = function (settings) {
-
-    var domInput, elmInputBox, elmInputWrapper, elmAutocompleteList, elmWrapperBox, elmMentionsOverlay, elmActiveAutoCompleteItem;
-    var mentionsCollection = [];
-    var autocompleteItemCollection = {};
-    var inputBuffer = [];
-    var currentDataQuery = '';
-    var mentionChar = "\u200B"; // zero width space
-
-    settings = $.extend(true, {}, defaultSettings, settings );
-
-    function initTextarea() {
-      elmInputBox = $(domInput);
-
-      if (elmInputBox.attr('data-mentions-input') == 'true') {
-        return;
-      }
-
-      elmInputWrapper = elmInputBox.parent();
-      elmWrapperBox = $(settings.templates.wrapper());
-      elmInputBox.wrapAll(elmWrapperBox);
-      elmWrapperBox = elmInputWrapper.find('> div').first();
-
-      elmInputBox.attr('data-mentions-input', 'true');
-      elmInputBox.bind('keydown', onInputBoxKeyDown);
-      elmInputBox.bind('keypress', onInputBoxKeyPress);
-      elmInputBox.bind('paste',onInputBoxPaste);
-      elmInputBox.bind('input', onInputBoxInput);
-      elmInputBox.bind('click', onInputBoxClick);
-      elmInputBox.bind('blur', onInputBoxBlur);
-
-      // 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', 'mousedown', onAutoCompleteItemClick);
-    }
-
-    function initMentionsOverlay() {
-      elmMentionsOverlay = $(settings.templates.mentionsOverlay());
-      elmMentionsOverlay.prependTo(elmWrapperBox);
-    }
-
-    function updateValues() {
-      var syntaxMessage = getInputBoxValue();
-
-      _.each(mentionsCollection, function (mention) {
-        var textSyntax = settings.templates.mentionItemSyntax(mention);
-        syntaxMessage = syntaxMessage.replace(mentionChar + mention.value, textSyntax);
-      });
-
-      var mentionText = utils.htmlEncode(syntaxMessage);
-
-      _.each(mentionsCollection, function (mention) {
-        var formattedMention = _.extend({}, mention, {value: mentionChar + utils.htmlEncode(mention.value)});
-        var textSyntax = settings.templates.mentionItemSyntax(formattedMention);
-        var textHighlight = settings.templates.mentionItemHighlight(formattedMention);
-
-        mentionText = mentionText.replace(textSyntax, textHighlight);
-      });
-
-      mentionText = mentionText.replace(/\n/g, '<br />');
-      mentionText = mentionText.replace(/ {2}/g, '&nbsp; ');
-
-      elmInputBox.data('messageText', syntaxMessage);
-      elmMentionsOverlay.find('div > div').html(mentionText);
-    }
-
-    function resetBuffer() {
-      inputBuffer = [];
-    }
-
-    function updateMentionsCollection() {
-      var inputText = getInputBoxValue();
-
-      mentionsCollection = _.reject(mentionsCollection, function (mention, index) {
-        return !mention.value || inputText.indexOf(mention.value) == -1;
-      });
-      mentionsCollection = _.compact(mentionsCollection);
-    }
-
-    function addMention(mention) {
-
-      var currentMessage = getInputBoxValue();
-
-      // Using a regex to figure out positions
-      var regex = new RegExp("\\" + settings.triggerChar + currentDataQuery, "gi");
-      regex.exec(currentMessage);
-
-      var startCaretPosition = regex.lastIndex - currentDataQuery.length - 1;
-      var currentCaretPosition = regex.lastIndex;
-
-      var start = currentMessage.substr(0, startCaretPosition);
-      var end = currentMessage.substr(currentCaretPosition, currentMessage.length);
-      var startEndIndex = (start + mention.value).length + 1;
-
-      mentionsCollection.push(mention);
-
-      // Cleaning before inserting the value, otherwise auto-complete would be triggered with "old" inputbuffer
-      resetBuffer();
-      currentDataQuery = '';
-      hideAutoComplete();
-
-      // Mentions & syntax message
-      var updatedMessageText = start + mentionChar + mention.value + end;
-      elmInputBox.val(updatedMessageText);
-      updateValues();
-
-      // Set correct focus and selection
-      elmInputBox.focus();
-      utils.setCaratPosition(elmInputBox[0], startEndIndex);
-    }
-
-    function getInputBoxValue() {
-      return $.trim(elmInputBox.val());
-    }
-
-    function onAutoCompleteItemClick(e) {
-      var elmTarget = $(this);
-      var mention = autocompleteItemCollection[elmTarget.attr('data-uid')];
-
-      addMention(mention);
-
-      return false;
-    }
-
-    function onInputBoxClick(e) {
-      resetBuffer();
-    }
-
-    function onInputBoxBlur(e) {
-      hideAutoComplete();
-    }
-
-    function onInputBoxPaste(e) {
-      pastedData = e.originalEvent.clipboardData.getData("text/plain");
-      dataArray = pastedData.split("");
-      _.each(dataArray, function(value) {
-        inputBuffer.push(value);
-      });
-    }
-    function onInputBoxInput(e) {
-      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) {
-      // Excluding ctrl+v from key press event in firefox
-      if (!((e.which === KEY.PASTE && e.ctrlKey) || (e.keyCode === KEY.BACKSPACE))) {
-        var typedValue = String.fromCharCode(e.which || e.keyCode);
-        inputBuffer.push(typedValue);
-      }
-    }
-
-    function onInputBoxKeyDown(e) {
-
-      // This also matches HOME/END on OSX which is CMD+LEFT, CMD+RIGHT
-      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;
-      }
-
-      if (e.keyCode == KEY.BACKSPACE) {
-        inputBuffer = inputBuffer.slice(0, -1 + inputBuffer.length); // Can't use splice, not available in IE
-        return;
-      }
-
-      if (!elmAutocompleteList.is(':visible')) {
-        return true;
-      }
-
-      switch (e.keyCode) {
-        case KEY.UP:
-        case KEY.DOWN:
-          var elmCurrentAutoCompleteItem = null;
-          if (e.keyCode == KEY.DOWN) {
-            if (elmActiveAutoCompleteItem && elmActiveAutoCompleteItem.length) {
-              elmCurrentAutoCompleteItem = elmActiveAutoCompleteItem.next();
-            } else {
-              elmCurrentAutoCompleteItem = elmAutocompleteList.find('li').first();
-            }
-          } else {
-            elmCurrentAutoCompleteItem = $(elmActiveAutoCompleteItem).prev();
-          }
-
-          if (elmCurrentAutoCompleteItem.length) {
-            selectAutoCompleteItem(elmCurrentAutoCompleteItem);
-          }
-
-          return false;
-
-        case KEY.RETURN:
-        case KEY.TAB:
-          if (elmActiveAutoCompleteItem && elmActiveAutoCompleteItem.length) {
-            elmActiveAutoCompleteItem.trigger('mousedown');
-            return false;
-          }
-
-          break;
-      }
-
-      return true;
-    }
-
-    function hideAutoComplete() {
-      elmActiveAutoCompleteItem = null;
-      elmAutocompleteList.empty().hide();
-    }
-
-    function selectAutoCompleteItem(elmItem) {
-      elmItem.addClass(settings.classes.autoCompleteItemActive);
-      elmItem.siblings().removeClass(settings.classes.autoCompleteItemActive);
-
-      elmActiveAutoCompleteItem = elmItem;
-    }
-
-    function populateDropdown(query, results) {
-      elmAutocompleteList.show();
-
-      // Filter items that has already been mentioned
-      var mentionValues = _.pluck(mentionsCollection, 'value');
-      results = _.reject(results, function (item) {
-        return _.include(mentionValues, item.name);
-      });
-
-      if (!results.length) {
-        hideAutoComplete();
-        return;
-      }
-
-      elmAutocompleteList.empty();
-      var elmDropDownList = $("<ul>").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)
-        })).attr('data-uid', itemUid);
-
-        if (index === 0) {
-          selectAutoCompleteItem(elmListItem);
-        }
-
-        if (settings.showAvatars) {
-          var elmIcon;
-
-          if (item.avatar) {
-            elmIcon = $(settings.templates.autocompleteListItemAvatar({ avatar : item.avatar }));
-          } else {
-            elmIcon = $(settings.templates.autocompleteListItemIcon({ icon : item.icon }));
-          }
-          elmIcon.prependTo(elmListItem);
-        }
-        elmListItem = elmListItem.appendTo(elmDropDownList);
-      });
-
-      elmAutocompleteList.show();
-      elmDropDownList.show();
-    }
-
-    function doSearch(query) {
-      if (query && query.length && query.length >= settings.minChars) {
-        settings.onDataRequest.call(this, 'search', query, function (responseData) {
-          populateDropdown(query, responseData);
-        });
-      }
-    }
-
-    function resetInput() {
-      elmInputBox.val('');
-      mentionsCollection = [];
-      updateValues();
-    }
-
-    // Public methods
-    return {
-      init : function (domTarget) {
-
-        domInput = domTarget;
-
-        initTextarea();
-        initAutocomplete();
-        initMentionsOverlay();
-        resetInput();
-
-        if( settings.prefillMention ) {
-          addMention( settings.prefillMention );
-        }
-
-      },
-
-      val : function (callback) {
-        if (!_.isFunction(callback)) {
-          return;
-        }
-
-        var value = mentionsCollection.length ? elmInputBox.data('messageText') : getInputBoxValue();
-        callback.call(this, value);
-      },
-
-      reset : function () {
-        resetInput();
-      },
-
-      getMentions : function (callback) {
-        if (!_.isFunction(callback)) {
-          return;
-        }
-
-        callback.call(this, mentionsCollection);
-      }
-    };
-  };
-
-  $.fn.mentionsInput = function (method, settings) {
-
-    var outerArguments = arguments;
-
-    if (typeof method === 'object' || !method) {
-      settings = method;
-    }
-
-    return this.each(function () {
-      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, this);
-
-      } else {
-        $.error('Method ' + method + ' does not exist');
-      }
-
-    });
-  };
-
-})(jQuery, _);
-// @license-end