]> git.mxchange.org Git - friendica.git/blob - js/autocomplete.js
rework autocomplete: initial commit
[friendica.git] / js / autocomplete.js
1 /**
2  * Red people autocomplete
3  *
4  * require jQuery, jquery.textcomplete
5  */
6 function contact_search(term, callback, backend_url, type) {
7
8         // Check if there is a cached result that contains the same information we would get with a full server-side search
9         var bt = backend_url+type;
10         if(!(bt in contact_search.cache)) contact_search.cache[bt] = {};
11
12         var lterm = term.toLowerCase(); // Ignore case
13         for(var t in contact_search.cache[bt]) {
14                 if(lterm.indexOf(t) >= 0) { // A more broad search has been performed already, so use those results
15                         // Filter old results locally
16                         var matching = contact_search.cache[bt][t].filter(function (x) { return (x.name.toLowerCase().indexOf(lterm) >= 0 || (typeof x.nick !== 'undefined' && x.nick.toLowerCase().indexOf(lterm) >= 0)); }); // Need to check that nick exists because groups don't have one
17                         matching.unshift({taggable:false, text: term, replace: term});
18                         setTimeout(function() { callback(matching); } , 1); // Use "pseudo-thread" to avoid some problems
19                         return;
20                 }
21         }
22
23         var postdata = {
24                 start:0,
25                 count:100,
26                 search:term,
27                 type:type,
28         };
29
30
31         $.ajax({
32                 type:'POST',
33                 url: backend_url,
34                 data: postdata,
35                 dataType: 'json',
36                 success: function(data){
37                         // Cache results if we got them all (more information would not improve results)
38                         // data.count represents the maximum number of items
39                         if(data.items.length -1 < data.count) {
40                                 contact_search.cache[bt][lterm] = data.items;
41                         }
42                         var items = data.items.slice(0);
43                         items.unshift({taggable:false, text: term, replace: term});
44                         callback(items);
45                 },
46         }).fail(function () {callback([]); }); // Callback must be invoked even if something went wrong.
47 }
48 contact_search.cache = {};
49
50
51 function contact_format(item) {
52         // Show contact information if not explicitly told to show something else
53         if(typeof item.text === 'undefined') {
54                 var desc = ((item.label) ? item.nick + ' ' + item.label : item.nick);
55                 if(typeof desc === 'undefined') desc = '';
56                 if(desc) desc = ' ('+desc+')';
57                 return "<div class='{0}' title='{4}'><img class='dropdown-menu-img-sm' src='{1}'><span class='contactname'>{2}</span><span class='dropdown-sub-text'>{3}</span><div class='clear'></div></div>".format(item.taggable, item.photo, item.name, desc, item.link);
58         }
59         else
60                 return "<div>" + item.text + "</div>";
61 }
62
63 function editor_replace(item) {
64         if(typeof item.replace !== 'undefined') {
65                 return '$1$2' + item.replace;
66         }
67
68         // $2 ensures that prefix (@,@!) is preserved
69         var id = item.id;
70          // 16 chars of hash should be enough. Full hash could be used if it can be done in a visually appealing way.
71         // 16 chars is also the minimum length in the backend (otherwise it's interpreted as a local id).
72         if(id.length > 16) 
73                 id = item.id.substring(0,16);
74
75         return '$1$2' + item.nick.replace(' ', '') + '+' + id + ' ';
76 }
77
78 function basic_replace(item) {
79         if(typeof item.replace !== 'undefined')
80                 return '$1'+item.replace;
81
82         return '$1'+item.name+' ';
83 }
84
85 function trim_replace(item) {
86         if(typeof item.replace !== 'undefined')
87                 return '$1'+item.replace;
88
89         return '$1'+item.name;
90 }
91
92
93 function submit_form(e) {
94         $(e).parents('form').submit();
95 }
96
97 /**
98  * jQuery plugin 'editor_autocomplete'
99  */
100 (function( $ ) {
101         $.fn.editor_autocomplete = function(backend_url) {
102
103                 // Autocomplete contacts
104                 contacts = {
105                         match: /(^|\s)(@\!*)([^ \n]+)$/,
106                         index: 3,
107                         search: function(term, callback) { contact_search(term, callback, backend_url, 'c'); },
108                         replace: editor_replace,
109                         template: contact_format,
110                 };
111
112                 smilies = {
113                         match: /(^|\s)(:[a-z]{2,})$/,
114                         index: 2,
115                         search: function(term, callback) { $.getJSON('/smilies/json').done(function(data) { callback($.map(data, function(entry) { return entry.text.indexOf(term) === 0 ? entry : null; })); }); },
116                         template: function(item) { return item.icon + item.text; },
117                         replace: function(item) { return "$1" + item.text + ' '; },
118                 };
119                 this.attr('autocomplete','off');
120                 this.textcomplete([contacts,smilies], {className:'acpopup', zIndex:1020});
121         };
122 })( jQuery );
123
124 /**
125  * jQuery plugin 'search_autocomplete'
126  */
127 (function( $ ) {
128         $.fn.search_autocomplete = function(backend_url) {
129                 // Autocomplete contacts
130                 contacts = {
131                         match: /(^@)([^\n]{2,})$/,
132                         index: 2,
133                         search: function(term, callback) { contact_search(term, callback, backend_url, 'x'); },
134                         replace: basic_replace,
135                         template: contact_format,
136                 };
137                 this.attr('autocomplete', 'off');
138                 var a = this.textcomplete([contacts], {className:'acpopup', maxCount:100, zIndex: 1020, appendTo:'nav'});
139                 a.on('textComplete:select', function(e, value, strategy) { submit_form(this); });
140         };
141 })( jQuery );
142
143 (function( $ ) {
144         $.fn.contact_autocomplete = function(backend_url, typ, autosubmit, onselect) {
145                 if(typeof typ === 'undefined') typ = '';
146                 if(typeof autosubmit === 'undefined') autosubmit = false;
147
148                 // Autocomplete contacts
149                 contacts = {
150                         match: /(^)([^\n]+)$/,
151                         index: 2,
152                         search: function(term, callback) { contact_search(term, callback, backend_url, typ); },
153                         replace: basic_replace,
154                         template: contact_format,
155                 };
156
157                 this.attr('autocomplete','off');
158                 var a = this.textcomplete([contacts], {className:'acpopup', zIndex:1020});
159
160                 if(autosubmit)
161                         a.on('textComplete:select', function(e,value,strategy) { submit_form(this); });
162
163                 if(typeof onselect !== 'undefined')
164                         a.on('textComplete:select', function(e, value, strategy) { onselect(value); });
165         };
166 })( jQuery );
167
168
169 (function( $ ) {
170         $.fn.name_autocomplete = function(backend_url, typ, autosubmit, onselect) {
171                 if(typeof typ === 'undefined') typ = '';
172                 if(typeof autosubmit === 'undefined') autosubmit = false;
173
174                 // Autocomplete contacts
175                 names = {
176                         match: /(^)([^\n]+)$/,
177                         index: 2,
178                         search: function(term, callback) { contact_search(term, callback, backend_url, typ); },
179                         replace: trim_replace,
180                         template: contact_format,
181                 };
182
183                 this.attr('autocomplete','off');
184                 var a = this.textcomplete([names], {className:'acpopup', zIndex:1020});
185
186                 if(autosubmit)
187                         a.on('textComplete:select', function(e,value,strategy) { submit_form(this); });
188
189                 if(typeof onselect !== 'undefined')
190                         a.on('textComplete:select', function(e, value, strategy) { onselect(value); });
191         };
192 })( jQuery );