Browse files

Merge pull request #6 from vytautask/master

Merged other forks into one. Now it has Ajax and words with no preceding character autocompletion support
  • Loading branch information...
2 parents 54c0554 + f13ed60 commit c695d986205b9bd2c004a759e8954d9afcace494 @abrimo committed Nov 23, 2012
Showing with 229 additions and 85 deletions.
  1. +51 −1 README.md
  2. +1 −1 src/autocomplete/editor_plugin.js
  3. +177 −83 src/autocomplete/editor_plugin_src.js
View
52 README.md
@@ -1,4 +1,51 @@
-AutoComplete for TinyMCE provides inline autocomplete in a style similar to Twitter or Facebook. The text you type in tinyMCE is checked against a list of specified options; if there is a match then you will see them appear in a list underneath the caret. This plugin was originally designed for PriorityCentre, a task and meeting management application developed by Mijura (https://mijura.com).
+This fork merged all other known forks into one. Then all noticed bugs were fixed to work with recent JQuery (1.8.2).
+
+This plugin now has: Ajax and all words completion support (not only the ones preceding with '@' or some other character).
+
+Example code for .NET MVC 3 controller method for getting Ajax autocompletion words list:
+```
+ public JsonResult GetWordCompletion(string q)
+ {
+ var jsonObj = new
+ {
+ ok = 1,
+ DATA = new List<object>
+ {
+ new { name = "autocomplete item 1" },
+ new { name = "autocomplete item 2" },
+ new { name = "autocomplete item 3" }
+ },
+ ERRORS = new int[0]
+ };
+
+ return Json(jsonObj, JsonRequestBehavior.AllowGet);
+ }
+```
+
+Below you can find original readme...
+
+AJAX AutoComplete for TinyMCE provides inline autocomplete in a style similar to Twitter or Facebook. The text you type in tinyMCE is checked against a list of specified options; if there is a match then you will see them appear in a list underneath the caret. This plugin was originally designed for PriorityCentre, a task and meeting management application developed by Mijura (https://mijura.com).
+
+This fork has been updated to provide generation of autocomplete list on-the-fly with AJAX. Use at your own risk. Also you can visit my page: http://www.sib.li :-)
+
+Server should return data in this format:
+```
+{
+ "ok":1,
+ "DATA":
+ [
+ {"name":"autocomplete item 1"},
+ {"name":"autocomplete item 2"},
+ {"name":"autocomplete item 3"}
+ ],
+ "ERRORS":[]
+}
+```
+
+To configure use autocomplete_options_url setting (without autocomplete_options):
+```
+autocomplete_options_url: "/path/to/my/ajax.php",
+```
This plugin has been tested in Firefox, Chrome, Safari and Internet Explorer 9. It currently does not support IE7/8.
@@ -24,6 +71,9 @@ There are four parameters that need to be specified in your tinyMCE config:
]
3. autocomplete_trigger - You can specify a trigger character that must be type immediately before searching for options. The default trigger is '@'
4. autocomplete_end_option - Any text that you want to be added after the option. The caret will be placed between the option and this ending text. For example, you could specify 'end', in which case selecting an autocomplete option would insert: '@jane @end' with the caret placed in between (and including the trigger before the end option).
+5. autocomplete_min_length - The minimum number of characters a word needs to have before the autocomplete activates. Only active when autocomplete_trigger is ''. The default is 3.
+6. autocomplete_on_select - A function to call after an option is selected. The default is false.
+7. autocomplete_on_match - A function to call when text entered match only one option. The default is false.
## Configuration with TinyMCE
View
2 src/autocomplete/editor_plugin.js
@@ -1 +1 @@
-(function(){var e={};var d=40;var f=38;var a=27;var c=13;function b(g){return g.options==null?g.split(","):g.options;}tinymce.create("tinymce.plugins.AutoCompletePlugin",{setOptions:function(g){e.options=b(g);},getOptions:function(){return e.options;},init:function(l,k){e={list:o(),visible:false,cancelEnter:false,delimiter:l.getParam("autocomplete_delimiters","160,32").split(","),options:b(l.getParam("autocomplete_options","")),trigger:l.getParam("autocomplete_trigger","@"),enclosing:l.getParam("autocomplete_end_option","")};function n(y,B){if((!e.visible&&B.keyCode!=a&&B.keyCode!=c)||(B.keyCode!=d&&B.keyCode!=f&&B.keyCode!=c&&B.keyCode!=a)){var z=x(y);var A=[];if(z.length>0){var C=z.replace(e.trigger,"");A=j(C);if(A.length>0){q(A,C,y);r();}}if(z.length==0||A.length==0){w();}}}function s(y,z){if(z.keyCode==c&&e.cancelEnter){e.cancelEnter=false;return tinymce.dom.Event.cancel(z);}}function m(y,z){if(e.visible){if(z.keyCode==d){r();return tinymce.dom.Event.cancel(z);}if(z.keyCode==f){h();return tinymce.dom.Event.cancel(z);}if(z.keyCode==c){v(y,x(y));e.cancelEnter=true;return;}if(z.keyCode==a){w();return tinymce.dom.Event.cancel(z);}}}function i(y,z){w();}function q(E,H,G){var A="";var B=new RegExp("("+H+")");for(var D in E){if(E[D].key!=null){A+="<li data-value='"+E[D].key+"'>"+E[D].key.replace(B,"<mark>$1</mark>")+" "+E[D].description+"</li>";}else{A+="<li data-value='"+E[D]+"'>"+E[D].replace(B,"<mark>$1</mark>")+"</li>";}}jQuery(e.list).html(A);var y=jQuery(G.getContainer()).position();var I=jQuery(G.getContainer()).find(".mceToolbar").first();var z=jQuery(G.selection.getNode()).position();var F=0;var C=0;if(G.selection.getRng().getClientRects().length>0){F=G.selection.getRng().getClientRects()[0].top+G.selection.getRng().getClientRects()[0].height;C=G.selection.getRng().getClientRects()[0].left;}else{F=parseInt(jQuery(G.selection.getNode()).css("font-size"))*1.3+z.top;C=z.left;}jQuery(e.list).css("margin-top",y.top+I.innerHeight()+F);jQuery(e.list).css("margin-left",y.left+C);jQuery(e.list).css("display","block");e.visible=true;u(G);}function u(y){jQuery(e.list).find("li").hover(function(){jQuery(e.list).find("[data-selected=true]").attr("data-selected","false");jQuery(this).attr("data-selected","true");});jQuery(e.list).find("li").click(function(){v(y,x(y));});}function o(){var y=document.createElement("ul");jQuery(y).addClass("auto-list");document.body.appendChild(y);return y;}function w(){jQuery(e.list).css("display","none");e.visible=false;}function r(){var y=jQuery(e.list).find("[data-selected=true]");if(y.size()==0||y.next().size()==0){jQuery(e.list).find("li:first-child").attr("data-selected","true");}else{y.next().attr("data-selected","true");}y.attr("data-selected","false");}function h(){var y=jQuery(e.list).find("[data-selected=true]");if(y.size()==0||y.prev().size()==0){jQuery(e.list).find("li:last-child").attr("data-selected","true");}else{y.prev().attr("data-selected","true");}y.attr("data-selected","false");}function v(z,A){var E=jQuery(e.list).find("[data-selected=true]").attr("data-value");if(E==null){E=jQuery(e.list).find("li:first-child").attr("data-value");}var C=t(z.selection.getSel().anchorNode,"");var B=z.selection.getSel().anchorNode.textContent;var y=z.selection.getRng();y.setStart(y.startContainer,y.startOffset-A.length);z.selection.setRng(y);var F="";if(e.delimiter.length>0){F=String.fromCharCode(e.delimiter[0]);}z.selection.setContent(e.trigger+E+F);if(e.enclosing.length>0&&!g(C,B)){var D=z.selection.getBookmark();z.selection.setContent(F+e.trigger+e.enclosing);z.selection.moveToBookmark(D);}w();}function g(z,y){var B=e.trigger+e.enclosing;z=z.substr(y.length);var A=new RegExp(e.trigger+".{"+e.enclosing.length+"}","g").exec(z);if(A!=null&&A.length>0&&A[0]==B){return true;}return false;}function t(y,z){z+=y.textContent;if(y.nextSibling!=null){return t(y.nextSibling,z);}return z;}function j(A){var y=e.options;var B=[];for(var z in y){if(y[z].key==null&&(A.length==0||p(A,y[z]))){B.push(y[z]);}else{if(y[z].key!=null&&(A.length==0||p(A,y[z].key))){B.push(y[z]);}}}return B;}function p(z,y){return(y.match("^"+z)==z);}function x(z){var C=z.selection.getSel().focusNode==null?"":z.selection.getSel().focusNode.nodeValue;var A=z.selection.getSel().focusOffset;if(C==null||C.length==0){return"";}var y=0;for(var B=0;B<A;B++){if(e.delimiter.indexOf(C.charCodeAt(B).toString())!=-1){y=B+1;}}var D=C.substr(y,A-y);if(D.length>0&&D.charAt(0).toString()==e.trigger){return D;}return"";}l.onKeyUp.addToTop(n);l.onKeyDown.addToTop(m);l.onKeyPress.addToTop(s);l.onClick.add(i);},getInfo:function(){return{longname:"AutoComplete",author:"Mijura Pty Ltd",authorurl:"http://mijura.com",infourl:"http://blog.mijura.com",version:tinymce.majorVersion+"."+tinymce.minorVersion};}});tinymce.PluginManager.add("autocomplete",tinymce.plugins.AutoCompletePlugin);})();
+var c=null,i=!1;function j(f){return f.options==c&&"boolean"!=typeof f?f.split(","):f.options}var p={},v=[32,59,186,188,190]; tinymce.create("tinymce.plugins.AutoCompletePlugin",{Y:function(f){p.options=j(f)},L:function(){return p.options},P:function(f){function s(b,a){var d=matches,e="",f=RegExp("("+b+")"),g;for(g in d)e=d[g].key!=c?e+("<li data-value='"+d[g].key+"'>"+d[g].key.replace(f,"<mark>$1</mark>")+" "+d[g].description+"</li>"):e+("<li data-value='"+d[g]+"'>"+d[g].replace(f,"<mark>$1</mark>")+"</li>");jQuery(p.list).N(e);var d=jQuery(a.t()).position(),e=jQuery(a.t()).find(".mceToolbar").J(),f=jQuery(a.selection.u()).position(), k=g=0;0<a.selection.h().getClientRects().length?(g=a.selection.h().getClientRects()[0].top+a.selection.h().getClientRects()[0].height,k=a.selection.h().getClientRects()[0].left):(g=1.3*parseInt(jQuery(a.selection.u()).f("font-size"))+f.top,k=f.left);jQuery(p.list).f("margin-top",d.top+e.innerHeight()+g);jQuery(p.list).f("margin-left",d.left+k);jQuery(p.list).f("display","block");p.visible=!0;jQuery(p.list).find("li").M(function(){jQuery(p.list).find("[data-selected=true]").a("data-selected","false"); jQuery(this).a("data-selected","true")});jQuery(p.list).find("li").click(function(){t(a,m(a))})}function l(){jQuery(p.list).f("display","none");p.visible=i}function q(){var b=jQuery(p.list).find("[data-selected=true]");0==b.size()||0==b.next().size()?jQuery(p.list).find("li:first-child").a("data-selected","true"):b.next().a("data-selected","true");b.a("data-selected","false")}function t(b,a){var d=jQuery(p.list).find("[data-selected=true]").a("data-value");d==c&&(d=jQuery(p.list).find("li:first-child").a("data-value")); var e=u(b.selection.i().anchorNode,""),f=b.selection.i().anchorNode.textContent,g=b.selection.h();g.setStart(g.startContainer,g.startOffset-a.length);b.selection.Z(g);g="";0<p.m.length&&(g=String.fromCharCode(p.m[0]));b.selection.z(p.c+d.toString()+g);var k;if(k=0<p.g.length)k=p.c+p.g,e=e.substr(f.length),e=RegExp(p.c+".{"+p.g.length+"}","g").exec(e),k=!(e!=c&&0<e.length&&e[0]==k);k&&(e=b.selection.getBookmark(),b.selection.z(g+p.c+p.g),b.selection.moveToBookmark(e));l();p.e&&h.e.r(b,d);l()}function u(b, a){a+=b.textContent;return b.nextSibling!=c?u(b.nextSibling,a):a}function n(b){var a=p.options,d=[],e;for(e in a)a[e].key==c&&(0==b.length||a[e].match(RegExp("^"+b,"i")))?d.push(a[e]):a[e].key!=c&&(0==b.length||a[e].key.match(RegExp("^"+b,"i")))&&d.push(a[e]);return d}function m(b){var a=b.selection.i().focusNode==c?"":b.selection.i().focusNode.nodeValue,b=b.selection.i().focusOffset;if(a==c||0==a.length)return"";for(var d=0,e=0;e<b;e++)-1!=p.m.indexOf(a.charCodeAt(e).toString())&&(d=e+1);a=a.substr(d, b-d);b="";""==p.c?a.length>=p.v&&(b=a):0<a.length&&a.charAt(0).toString()==p.c&&(b=a);return b}var r=document.createElement("ul");jQuery(r).D("auto-list");document.body.appendChild(r);p={list:r,visible:i,l:i,m:f.b("autocomplete_delimiters","160,32").split(","),options:j(f.b("autocomplete_options","")),p:j(f.b("autocomplete_options_url",i)),c:f.b("autocomplete_trigger","@"),g:f.b("autocomplete_end_option",""),v:f.b("autocomplete_min_length","3"),e:f.b("autocomplete_on_select",i),d:f.b("autocomplete_on_match", i)};var h=this;p.e&&(h.e=new tinymce.A.q(h),h.e.add(function(b,a){b.s("autocomplete_on_select",b,a)}));p.d&&(h.d=new tinymce.A.q(h),h.d.add(function(b,a){b.s("autocomplete_on_match",b,a)}));f.X.o(function(b,a){if(!p.visible&&27!=a.keyCode&&13!=a.keyCode||40!=a.keyCode&&38!=a.keyCode&&13!=a.keyCode&&27!=a.keyCode){var d=m(b),e=n(d);if(0<d.length){var h=d.replace(p.c,"");p.p?1>=h.length||jQuery.F({type:"GET",url:p.p,I:i,data:"q="+h,$:function(a){if(a.T&&a.j){var b=[],d;for(d in a.j)a.j[d].name&&b.push(a.j[d].name); p.options=b;matches=n(h);0<matches.length&&(s(h,f),q())}},error:function(){}}):(matches=n(h),0<matches.length&&(s(h,f),q()))}(0==d.length||0==e.length)&&l()}});f.V.o(function(b,a){if(p.visible){if(40==a.keyCode)return q(),tinymce.n.k.cancel(a);if(38==a.keyCode){var d=jQuery(p.list).find("[data-selected=true]");0==d.size()||0==d.w().size()?jQuery(p.list).find("li:last-child").a("data-selected","true"):d.w().a("data-selected","true");d.a("data-selected","false");return tinymce.n.k.cancel(a)}if(13== a.keyCode)return t(b,m(b)),p.l=!0,i;if(27==a.keyCode)return l(),tinymce.n.k.cancel(a);if(p.d&&v.indexOf(a.keyCode)){var d=m(b),e=n(d),f=RegExp("^"+e[0]+"$","i");1==e.length&&d.match(f)&&h.d.r(b,e[0])}}});f.W.o(function(b,a){if(13==a.keyCode&&p.l)return p.l=i,tinymce.n.k.cancel(a)});f.U.add(function(){l()})},K:function(){return{Q:"AutoComplete",G:"Mijura Pty Ltd",H:"http://mijura.com",O:"http://blog.mijura.com",version:tinymce.R+"."+tinymce.S}}});tinymce.C.add("autocomplete",tinymce.plugins.B);
View
260 src/autocomplete/editor_plugin_src.js
@@ -10,7 +10,7 @@
* you will see them appear in a list underneath the caret.
*
* Configuration:
- * There are four parameters that need to be specified in your tinyMCE config:
+ * Parameters that we can use in tinyMCE config:
* 1\ autocomplete_delimiters - A CSV list of delimiters (ASCII codes) on which
* to split text entered into tinyMCE. In most cases you will want to
* split text by spaces, in which case you would specify '160,32'. 32 is
@@ -27,72 +27,143 @@
* text. For example, you could specify 'end', in which case selecting
* an autocomplete option would insert: '@jane @end' with the caret
* placed in between (and including the trigger before the end option).
- *
+ * 5\ autocomplete_min_length - The minimum number of characters a word needs to have
+ * before the autocomplete activates. Only active when autocomplete_trigger
+ * is ''. The default is 3.
+ * 6\ autocomplete_on_select - A function to call after an option is selected.
+ * The default is false.
+ * 7\ autocomplete_on_match - A function to call when text entered match only one option.
+ * The default is false.
+ *
* Support:
* You are welcome to use this plugin at your own risk. It is currently
* being maintained on GitHub where you can submit issues / feature requests.
*/
-(function() {
+(function () {
var autocomplete_data = {};
var DOWN_ARROW_KEY = 40;
var UP_ARROW_KEY = 38;
var ESC_KEY = 27;
var ENTER_KEY = 13;
-
- function parseOptions( param )
- {
- return param.options == null ? param.split(",") : param.options;
+ var END_WORD_KEYS = [32, 59, 186, 188, 190];
+
+ function parseOptions(param) {
+ return param.options == null && typeof param != "boolean" ? param.split(",") : param.options;
}
-
+
tinymce.create('tinymce.plugins.AutoCompletePlugin', {
-
- setOptions : function( param )
- {
- autocomplete_data.options = parseOptions( param );
+
+ setOptions: function (param) {
+ autocomplete_data.options = parseOptions(param);
},
-
- getOptions : function()
- {
+
+ getOptions: function () {
return autocomplete_data.options;
},
-
- init : function(ed, url) {
-
+
+ init: function (ed, url) {
+
autocomplete_data = {
list: createOptionList(),
visible: false,
cancelEnter: false,
delimiter: ed.getParam('autocomplete_delimiters', '160,32').split(","),
- options: parseOptions( ed.getParam('autocomplete_options', '') ),
+ options: parseOptions(ed.getParam('autocomplete_options', '')),
+ optionsUrl: parseOptions(ed.getParam('autocomplete_options_url', false)),
trigger: ed.getParam('autocomplete_trigger', '@'),
- enclosing: ed.getParam('autocomplete_end_option', '')
+ enclosing: ed.getParam('autocomplete_end_option', ''),
+ minLength: ed.getParam('autocomplete_min_length', '3'),
+ onSelect: ed.getParam('autocomplete_on_select', false),
+ onMatch: ed.getParam('autocomplete_on_match', false)
};
-
-
+
+ var t = this;
+
+ // Setup plugin event
+ if (autocomplete_data.onSelect) {
+ t.onSelect = new tinymce.util.Dispatcher(t);
+ t.onSelect.add(function (ed, selected) {
+ ed.execCallback('autocomplete_on_select', ed, selected);
+ });
+ }
+ if (autocomplete_data.onMatch) {
+ t.onMatch = new tinymce.util.Dispatcher(t);
+ t.onMatch.add(function (ed, match) {
+ ed.execCallback('autocomplete_on_match', ed, match);
+ });
+ }
+
/**
* Search for autocomplete options after text is entered and display the
* option list if any matches are found.
*/
function keyUpEvent(ed, e) {
if ((!autocomplete_data.visible && e.keyCode != ESC_KEY && e.keyCode != ENTER_KEY) || (e.keyCode != DOWN_ARROW_KEY && e.keyCode != UP_ARROW_KEY && e.keyCode != ENTER_KEY && e.keyCode != ESC_KEY)) {
var currentWord = getCurrentWord(ed);
- var matches = [];
+ var matches = matchingOptions(currentWord);
if (currentWord.length > 0) {
- var wordLessTrigger = currentWord.replace(autocomplete_data.trigger,"");
- matches = matchingOptions(wordLessTrigger);
-
- if (matches.length > 0) {
- displayOptionList(matches, wordLessTrigger, ed);
- highlightNextOption();
- }
+ populateList(currentWord);
}
if (currentWord.length == 0 || matches.length == 0) {
hideOptionList();
}
}
}
-
+
+
+ /**
+ * Populates autocomplete list with matched words.
+ *
+ */
+ function populateList(currentWord) {
+ var wordLessTrigger = currentWord.replace(autocomplete_data.trigger, "");
+
+ if (autocomplete_data.optionsUrl) {
+ if (wordLessTrigger.length <= 1)
+ return false;
+
+ jQuery.ajax({
+ type: "GET",
+ url: autocomplete_data.optionsUrl,
+ cache: false,
+ data: "q=" + wordLessTrigger,
+ success: function (data) {
+ //hideLoading();
+ if (data.ok && data.DATA) {
+ var options = [];
+ for (var i in data.DATA) {
+ if (data.DATA[i].name)
+ options.push(data.DATA[i].name);
+ }
+ autocomplete_data.options = options;
+
+ matches = matchingOptions(wordLessTrigger);
+
+ if (matches.length > 0) {
+ displayOptionList(matches, wordLessTrigger, ed);
+ highlightNextOption();
+ }
+ } else {
+ // No data
+ }
+ },
+ error: function (jqXHR, textStatus) {
+ // Error
+ }
+ }); // ajax
+
+ } else {
+ matches = matchingOptions(wordLessTrigger);
+
+ if (matches.length > 0) {
+ displayOptionList(matches, wordLessTrigger, ed);
+ highlightNextOption();
+ }
+ }
+ } // populateList
+
+
/**
* Prevent return from adding a new line after selecting an option.
*/
@@ -102,7 +173,7 @@
return tinymce.dom.Event.cancel(e);
}
}
-
+
/**
* Handle navigation inside the option list when it is visible.
* These events should not propagate to the editor.
@@ -120,16 +191,25 @@
if (e.keyCode == ENTER_KEY) {
selectOption(ed, getCurrentWord(ed));
autocomplete_data.cancelEnter = true;
- return; // the enter evet needs to be cancelled on keypress so
- // it doesn't register a carriage return
+ return false; // the enter evet needs to be cancelled on keypress so
+ // it doesn't register a carriage return
}
if (e.keyCode == ESC_KEY) {
hideOptionList();
return tinymce.dom.Event.cancel(e);
}
+ // onMatch callback
+ if (autocomplete_data.onMatch && END_WORD_KEYS.indexOf(e.keyCode)) {
+ var word = getCurrentWord(ed);
+ var matches = matchingOptions(word);
+ var completeMatch = new RegExp("^" + matches[0] + "$", "i");
+ if (matches.length == 1 && word.match(completeMatch)) {
+ t.onMatch.dispatch(ed, matches[0]);
+ }
+ }
}
}
-
+
function clickEvent(ed, e) {
hideOptionList();
}
@@ -146,15 +226,15 @@
for (var i in matches) {
- if( matches[i].key != null ) {
- matchesList += "<li data-value='" + matches[i].key + "'>" + matches[i].key.replace(highlightRegex,"<mark>$1</mark>") +" " + matches[i].description + "</li>";
+ if (matches[i].key != null) {
+ matchesList += "<li data-value='" + matches[i].key + "'>" + matches[i].key.replace(highlightRegex, "<mark>$1</mark>") + " " + matches[i].description + "</li>";
}
else {
- matchesList += "<li data-value='" + matches[i] + "'>" + matches[i].replace(highlightRegex,"<mark>$1</mark>") + "</li>";
+ matchesList += "<li data-value='" + matches[i] + "'>" + matches[i].replace(highlightRegex, "<mark>$1</mark>") + "</li>";
}
}
jQuery(autocomplete_data.list).html(matchesList);
-
+
// work out the position of the caret
var tinymcePosition = jQuery(ed.getContainer()).position();
var toolbarPosition = jQuery(ed.getContainer()).find(".mceToolbar").first();
@@ -168,58 +248,58 @@
textareaTop = parseInt(jQuery(ed.selection.getNode()).css("font-size")) * 1.3 + nodePosition.top;
textareaLeft = nodePosition.left;
}
-
+
jQuery(autocomplete_data.list).css("margin-top", tinymcePosition.top + toolbarPosition.innerHeight() + textareaTop);
jQuery(autocomplete_data.list).css("margin-left", tinymcePosition.left + textareaLeft);
jQuery(autocomplete_data.list).css("display", "block");
autocomplete_data.visible = true;
optionListEventHandlers(ed);
}
-
+
/**
* Allow a user to select an option by clicking with the mouse and
* highlighting the options on hover.
*/
function optionListEventHandlers(ed) {
- jQuery(autocomplete_data.list).find("li").hover(function() {
- jQuery(autocomplete_data.list).find("[data-selected=true]").attr("data-selected","false");
- jQuery(this).attr("data-selected","true");
+ jQuery(autocomplete_data.list).find("li").hover(function () {
+ jQuery(autocomplete_data.list).find("[data-selected=true]").attr("data-selected", "false");
+ jQuery(this).attr("data-selected", "true");
});
- jQuery(autocomplete_data.list).find("li").click(function() {
+ jQuery(autocomplete_data.list).find("li").click(function () {
selectOption(ed, getCurrentWord(ed));
});
}
-
+
function createOptionList() {
var ulContainer = document.createElement("ul");
jQuery(ulContainer).addClass("auto-list");
document.body.appendChild(ulContainer);
return ulContainer;
}
-
+
function hideOptionList() {
jQuery(autocomplete_data.list).css("display", "none");
autocomplete_data.visible = false;
}
-
+
function highlightNextOption() {
var current = jQuery(autocomplete_data.list).find("[data-selected=true]");
if (current.size() == 0 || current.next().size() == 0) {
- jQuery(autocomplete_data.list).find("li:first-child").attr("data-selected","true");
+ jQuery(autocomplete_data.list).find("li:first-child").attr("data-selected", "true");
} else {
- current.next().attr("data-selected","true");
+ current.next().attr("data-selected", "true");
}
- current.attr("data-selected","false");
+ current.attr("data-selected", "false");
}
-
+
function highlightPreviousOption() {
var current = jQuery(autocomplete_data.list).find("[data-selected=true]");
if (current.size() == 0 || current.prev().size() == 0) {
- jQuery(autocomplete_data.list).find("li:last-child").attr("data-selected","true");
+ jQuery(autocomplete_data.list).find("li:last-child").attr("data-selected", "true");
} else {
- current.prev().attr("data-selected","true");
+ current.prev().attr("data-selected", "true");
}
- current.attr("data-selected","false");
+ current.attr("data-selected", "false");
}
/**
@@ -233,44 +313,50 @@
if (current == null) {
current = jQuery(autocomplete_data.list).find("li:first-child").attr("data-value");
}
-
- var content = restOfContent(ed.selection.getSel().anchorNode,"");
+
+ var content = restOfContent(ed.selection.getSel().anchorNode, "");
var currentNode = ed.selection.getSel().anchorNode.textContent;
-
+
// modify the range to replace overwrite the option text that has already been entered
var range = ed.selection.getRng();
range.setStart(range.startContainer, range.startOffset - matchedText.length);
ed.selection.setRng(range);
-
+
// insert the trigger, selected option and following delimiter
var delim = "";
if (autocomplete_data.delimiter.length > 0) {
delim = String.fromCharCode(autocomplete_data.delimiter[0]);
}
- ed.selection.setContent(autocomplete_data.trigger + current + delim);
-
+ ed.selection.setContent(autocomplete_data.trigger + current.toString() + delim);
+
// insert the enclosing text if it has not already been added
if (autocomplete_data.enclosing.length > 0 && !closingTextExists(content, currentNode)) {
var middleBookmark = ed.selection.getBookmark();
ed.selection.setContent(delim + autocomplete_data.trigger + autocomplete_data.enclosing);
- ed.selection.moveToBookmark(middleBookmark);
+ ed.selection.moveToBookmark(middleBookmark);
+ }
+ hideOptionList();
+
+ // onSelect callback
+ if (autocomplete_data.onSelect) {
+ t.onSelect.dispatch(ed, current);
}
hideOptionList();
}
-
+
/**
* Check if the enclosing string has already been placed past the current node.
*/
function closingTextExists(content, currentNode) {
var enclosed = autocomplete_data.trigger + autocomplete_data.enclosing;
content = content.substr(currentNode.length);
- var matches = new RegExp(autocomplete_data.trigger + ".{" + autocomplete_data.enclosing.length + "}","g").exec(content);
+ var matches = new RegExp(autocomplete_data.trigger + ".{" + autocomplete_data.enclosing.length + "}", "g").exec(content);
if (matches != null && matches.length > 0 && matches[0] == enclosed) {
return true;
}
return false;
}
-
+
/**
* Recursively find all of the content past (and including) the caret node.
* This doesn't appear to be available any other way.
@@ -280,28 +366,29 @@
if (anchorNode.nextSibling != null) {
return restOfContent(anchorNode.nextSibling, content);
}
- return content;
+ return content;
}
-
+
/**
* Find all options whose beginning matches the currently entered text.
*/
function matchingOptions(currentWord) {
var options = autocomplete_data.options;
var matches = [];
for (var i in options) {
- if ( options[i].key == null && (currentWord.length == 0 || beginningOfWordMatches(currentWord, options[i]))) {
+ if (options[i].key == null && (currentWord.length == 0 || beginningOfWordMatches(currentWord, options[i]))) {
matches.push(options[i]);
}
- else if( options[i].key != null && (currentWord.length == 0 || beginningOfWordMatches(currentWord, options[i].key))) {
+ else if (options[i].key != null && (currentWord.length == 0 || beginningOfWordMatches(currentWord, options[i].key))) {
matches.push(options[i]);
}
}
return matches;
}
-
+
function beginningOfWordMatches(beginning, option) {
- return (option.match("^" + beginning) == beginning);
+ var test = new RegExp("^" + beginning, "i");
+ return (option.match(test));
}
/**
@@ -317,14 +404,21 @@
var lastDelimiter = 0;
for (var i = 0; i < positionInNode; i++) {
if (autocomplete_data.delimiter.indexOf(nodeText.charCodeAt(i).toString()) != -1) {
- lastDelimiter = i+1;
+ lastDelimiter = i + 1;
}
}
- var word = nodeText.substr(lastDelimiter, positionInNode-lastDelimiter);
- if (word.length > 0 && word.charAt(0).toString() == autocomplete_data.trigger) {
- return word;
+ var word = nodeText.substr(lastDelimiter, positionInNode - lastDelimiter);
+ var retWord = "";
+ if (autocomplete_data.trigger == '') {
+ if (word.length >= autocomplete_data.minLength) {
+ retWord = word;
+ }
+ } else {
+ if (word.length > 0 && word.charAt(0).toString() == autocomplete_data.trigger) {
+ retWord = word;
+ }
}
- return "";
+ return retWord;
}
ed.onKeyUp.addToTop(keyUpEvent);
@@ -333,17 +427,17 @@
ed.onClick.add(clickEvent);
},
- getInfo : function() {
+ getInfo: function () {
return {
- longname : 'AutoComplete',
- author : 'Mijura Pty Ltd',
- authorurl : 'http://mijura.com',
- infourl : 'http://blog.mijura.com',
- version : tinymce.majorVersion + "." + tinymce.minorVersion
+ longname: 'AutoComplete',
+ author: 'Mijura Pty Ltd',
+ authorurl: 'http://mijura.com',
+ infourl: 'http://blog.mijura.com',
+ version: tinymce.majorVersion + "." + tinymce.minorVersion
};
}
});
tinymce.PluginManager.add('autocomplete',
tinymce.plugins.AutoCompletePlugin);
-})();
+})();

0 comments on commit c695d98

Please sign in to comment.