Skip to content

Commit

Permalink
Merge pull request #203 from mjtko/feature/atwho-regex-improvements
Browse files Browse the repository at this point in the history
Mention and emoji popups should only occur at start of line or after whitespace
  • Loading branch information
fusion94 committed Mar 11, 2013
2 parents 374e568 + bd8fbc2 commit 804b508
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 28 deletions.
Expand Up @@ -13,7 +13,7 @@ class Kandan.Plugins.Emojis
}

@attachToChatbox: ->
$(".chat-input").atwho ':([a-zA-Z0-9_+-]+)',
$(".chat-input").atwho '(?:^|\\s):',
data: @emojis
tpl: @options.atWhoTemplate
limit: 10
20 changes: 10 additions & 10 deletions app/assets/javascripts/backbone/plugins/mentions.coffee
Expand Up @@ -7,19 +7,19 @@ class Kandan.Plugins.Mentions
template: _.template '''<span class="mention"><%= mention %></span>'''

@init: ()->
Kandan.Data.ActiveUsers.registerCallback "change", (data)=>
Kandan.Data.ActiveUsers.registerCallback "change", (data)=>
@initUsersMentions(data.extra.active_users)

Kandan.Modifiers.register @options.regex, (message, state) =>
for mention in message.content.match(@options.regex)
replacement = @options.template({mention: mention})
message.content = message.content.replace(mention, replacement)
replacement = @options.template({mention: mention})
message.content = message.content.replace(mention, replacement)

return Kandan.Helpers.Activities.buildFromMessageTemplate(message)

return Kandan.Helpers.Activities.buildFromMessageTemplate(message)

@initUsersMentions: (activeUsers)->
users = _.map activeUsers, (user)->
user.username
users.push "all"
$(".chat-input").atwho("@", {data: users})
return
users = _.map activeUsers, (user)->
user.username
users.push "all"
$(".chat-input").atwho("(?:^|\\s)@", {data: users})
return
179 changes: 162 additions & 17 deletions app/assets/javascripts/lib/jquery.atwho.js
Expand Up @@ -20,7 +20,7 @@
var Controller, DEFAULT_CALLBACKS, DEFAULT_TPL, KEY_CODE, Mirror, View;
Mirror = (function() {

Mirror.prototype.css_attr = ["overflowY", "height", "width", "paddingTop", "paddingLeft", "paddingRight", "paddingBottom", "marginTop", "marginLeft", "marginRight", "marginBottom", 'fontFamily', 'borderStyle', 'borderWidth', 'wordWrap', 'fontSize', 'lineHeight', 'overflowX'];
Mirror.prototype.css_attr = ["overflowY", "height", "width", "paddingTop", "paddingLeft", "paddingRight", "paddingBottom", "marginTop", "marginLeft", "marginRight", "marginBottom", "fontFamily", "borderStyle", "borderWidth", "wordWrap", "fontSize", "lineHeight", "overflowX", "text-align"];

function Mirror($inputor) {
this.$inputor = $inputor;
Expand Down Expand Up @@ -75,6 +75,9 @@
};
DEFAULT_CALLBACKS = {
data_refactor: function(data) {
if (!$.isArray(data)) {
return data;
}
return $.map(data, function(item, k) {
if (!$.isPlainObject(item)) {
item = {
Expand Down Expand Up @@ -115,8 +118,13 @@
sorter: function(query, items, search_key) {
var item, results, text, _i, _len;
if (!query) {
items;

return items.sort(function(a, b) {
if (a[search_key].toLowerCase() > b[search_key].toLowerCase()) {
return 1;
} else {
return -1;
}
});
}
results = [];
for (_i = 0, _len = items.length; _i < _len; _i++) {
Expand Down Expand Up @@ -187,11 +195,7 @@
current_settings = {};
current_settings = $.isPlainObject(flag) ? this.common_settings = $.extend({}, this.common_settings, flag) : !this.settings[flag] ? this.settings[flag] = $.extend({}, settings) : this.settings[flag] = $.extend({}, this.settings[flag], settings);
data = current_settings["data"];
if (typeof data === "string") {
current_settings["data"] = data;
} else if (data) {
current_settings["data"] = this.callbacks("data_refactor").call(this, data);
}
current_settings["data"] = this.callbacks("data_refactor").call(this, data);
return this;
};

Expand Down Expand Up @@ -373,21 +377,33 @@
return this.view.render(data);
};

Controller.prototype.remote_call = function(data, query) {
var params, _callback;
params = {
q: query.text,
limit: this.get_opt("limit")
};
_callback = function(data) {
this.reg(this.current_flag, {
data: data
});
return this.render_view(this.data());
};
_callback = $.proxy(_callback, this);
return this.callbacks('remote_filter').call(this, params, data, _callback);
};

Controller.prototype.look_up = function() {
var data, origin_data, params, query, search_key;
var data, query, search_key;
query = this.catch_query();
if (!query) {
return false;
}
origin_data = this.get_opt("data");
data = this.data();
search_key = this.get_opt("search_key");
if (typeof origin_data === "string") {
params = {
q: query.text,
limit: this.get_opt("limit")
};
this.callbacks('remote_filter').call(this, params, origin_data, $.proxy(this.render_view, this));
} else if ((data = this.callbacks('filter').call(this, query.text, origin_data, search_key))) {
if (typeof data === "string") {
this.remote_call(data, query);
} else if ((data = this.callbacks('filter').call(this, query.text, data, search_key))) {
this.render_view(data);
} else {
this.view.hide();
Expand Down Expand Up @@ -560,3 +576,132 @@
});

}).call(this);


/*
Implement Github like autocomplete mentions
http://ichord.github.com/At.js
Copyright (c) 2013 chord.luo@gmail.com
Licensed under the MIT license.
*/


/*
本插件操作 textarea 或者 input 内的插入符
只实现了获得插入符在文本框中的位置,我设置
插入符的位置.
*/


(function() {

(function(factory) {
if (typeof exports === 'object') {
return factory(require('jquery'));
} else if (typeof define === 'function' && define.amd) {
return define(['jquery']);
} else {
return factory(window.jQuery);
}
})(function($) {
var getCaretPos, setCaretPos;
getCaretPos = function(inputor) {
var end, endRange, len, normalizedValue, pos, range, start, textInputRange;
if (document.selection) {
/*
#assume we select "HATE" in the inputor such as textarea -> { }.
* start end-point.
* /
* < I really [HATE] IE > between the brackets is the selection range.
* \
* end end-point.
*/

range = document.selection.createRange();
pos = 0;
if (range && range.parentElement() === inputor) {
normalizedValue = inputor.value.replace(/\r\n/g, "\n");
/* SOMETIME !!!
"/r/n" is counted as two char.
one line is two, two will be four. balalala.
so we have to using the normalized one's length.;
*/

len = normalizedValue.length;
/*
<[ I really HATE IE ]>:
the whole content in the inputor will be the textInputRange.
*/

textInputRange = inputor.createTextRange();
/* _here must be the position of bookmark.
/
<[ I really [HATE] IE ]>
[---------->[ ] : this is what moveToBookmark do.
< I really [[HATE] IE ]> : here is result.
\ two brackets in should be in line.
*/

textInputRange.moveToBookmark(range.getBookmark());
endRange = inputor.createTextRange();
/* [--------------------->[] : if set false all end-point goto end.
< I really [[HATE] IE []]>
*/

endRange.collapse(false);
/*
___VS____
/ \
< I really [[HATE] IE []]>
\_endRange end-point.
" > -1" mean the start end-point will be the same or right to the end end-point
* simplelly, all in the end.
*/

if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) {
start = end = len;
} else {
/*
I really |HATE] IE ]>
<-|
I really[ [HATE] IE ]>
<-[
I reall[y [HATE] IE ]>
will return how many unit have moved.
*/

start = -textInputRange.moveStart("character", -len);
end = -textInputRange.moveEnd("character", -len);
}
}
} else {
start = inputor.selectionStart;
}
return start;
};
setCaretPos = function(inputor, pos) {
var range;
if (document.selection) {
range = inputor.createTextRange();
range.move("character", pos);
return range.select();
} else {
return inputor.setSelectionRange(pos, pos);
}
};
return $.fn.caretPos = function(pos) {
var inputor;
inputor = this[0];
inputor.focus();
if (pos) {
return setCaretPos(inputor, pos);
} else {
return getCaretPos(inputor);
}
};
});

}).call(this);

0 comments on commit 804b508

Please sign in to comment.