##// END OF EJS Templates
codemirror: move some code outside of helpers for reuse
ergo -
r874:6903ccc9 default
parent child Browse files
Show More
@@ -29,6 +29,178 b' cmLog.setLevel(Logger.OFF);'
29 29 //global cache for inline forms
30 30 var userHintsCache = {};
31 31
32 // global timer, used to cancel async loading
33 var CodeMirrorLoadUserHintTimer;
34
35 var escapeRegExChars = function(value) {
36 return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
37 };
38
39 /**
40 * Load hints from external source returns an array of objects in a format
41 * that hinting lib requires
42 * @returns {Array}
43 */
44 var CodeMirrorLoadUserHints = function(query, triggerHints) {
45 cmLog.debug('Loading mentions users via AJAX');
46 var _users = [];
47 $.ajax({
48 type: 'GET',
49 data: {query: query},
50 url: pyroutes.url('user_autocomplete_data'),
51 headers: {'X-PARTIAL-XHR': true},
52 async: true
53 })
54 .done(function(data) {
55 var tmpl = '<img class="gravatar" src="{0}"/>{1}';
56 $.each(data.suggestions, function(i) {
57 var userObj = data.suggestions[i];
58
59 if (userObj.username !== "default") {
60 _users.push({
61 text: userObj.username + " ",
62 org_text: userObj.username,
63 displayText: userObj.value_display, // search that field
64 // internal caches
65 _icon_link: userObj.icon_link,
66 _text: userObj.value_display,
67
68 render: function(elt, data, completion) {
69 var el = document.createElement('div');
70 el.className = "CodeMirror-hint-entry";
71 el.innerHTML = tmpl.format(
72 completion._icon_link, completion._text);
73 elt.appendChild(el);
74 }
75 });
76 }
77 });
78 cmLog.debug('Mention users loaded');
79 // set to global cache
80 userHintsCache[query] = _users;
81 triggerHints(userHintsCache[query]);
82 })
83 .fail(function(data, textStatus, xhr) {
84 alert("error processing request: " + textStatus);
85 });
86 };
87
88 /**
89 * filters the results based on the current context
90 * @param users
91 * @param context
92 * @returns {Array}
93 */
94 var CodeMirrorFilterUsers = function(users, context) {
95 var MAX_LIMIT = 10;
96 var filtered_users = [];
97 var curWord = context.string;
98
99 cmLog.debug('Filtering users based on query:', curWord);
100 $.each(users, function(i) {
101 var match = users[i];
102 var searchText = match.displayText;
103
104 if (!curWord ||
105 searchText.toLowerCase().lastIndexOf(curWord) !== -1) {
106 // reset state
107 match._text = match.displayText;
108 if (curWord) {
109 // do highlighting
110 var pattern = '(' + escapeRegExChars(curWord) + ')';
111 match._text = searchText.replace(
112 new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
113 }
114
115 filtered_users.push(match);
116 }
117 // to not return to many results, use limit of filtered results
118 if (filtered_users.length > MAX_LIMIT) {
119 return false;
120 }
121 });
122
123 return filtered_users;
124 };
125
126 var CodeMirrorMentionHint = function(editor, callback, options) {
127 var cur = editor.getCursor();
128 var curLine = editor.getLine(cur.line).slice(0, cur.ch);
129
130 // match on @ +1char
131 var tokenMatch = new RegExp(
132 '(^@| @)([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]*)$').exec(curLine);
133
134 var tokenStr = '';
135 if (tokenMatch !== null && tokenMatch.length > 0){
136 tokenStr = tokenMatch[0].strip();
137 } else {
138 // skip if we didn't match our token
139 return;
140 }
141
142 var context = {
143 start: (cur.ch - tokenStr.length) + 1,
144 end: cur.ch,
145 string: tokenStr.slice(1),
146 type: null
147 };
148
149 // case when we put the @sign in fron of a string,
150 // eg <@ we put it here>sometext then we need to prepend to text
151 if (context.end > cur.ch) {
152 context.start = context.start + 1; // we add to the @ sign
153 context.end = cur.ch; // don't eat front part just append
154 context.string = context.string.slice(1, cur.ch - context.start);
155 }
156
157 cmLog.debug('Mention context', context);
158
159 var triggerHints = function(userHints){
160 return callback({
161 list: CodeMirrorFilterUsers(userHints, context),
162 from: CodeMirror.Pos(cur.line, context.start),
163 to: CodeMirror.Pos(cur.line, context.end)
164 });
165 };
166
167 var queryBasedHintsCache = undefined;
168 // if we have something in the cache, try to fetch the query based cache
169 if (userHintsCache !== {}){
170 queryBasedHintsCache = userHintsCache[context.string];
171 }
172
173 if (queryBasedHintsCache !== undefined) {
174 cmLog.debug('Users loaded from cache');
175 triggerHints(queryBasedHintsCache);
176 } else {
177 // this takes care for async loading, and then displaying results
178 // and also propagates the userHintsCache
179 window.clearTimeout(CodeMirrorLoadUserHintTimer);
180 CodeMirrorLoadUserHintTimer = setTimeout(function() {
181 CodeMirrorLoadUserHints(context.string, triggerHints);
182 }, 300);
183 }
184 };
185
186 var CodeMirrorCompleteAfter = function(cm, pred) {
187 var options = {
188 completeSingle: false,
189 async: true,
190 closeOnUnfocus: true
191 };
192 var cur = cm.getCursor();
193 setTimeout(function() {
194 if (!cm.state.completionActive) {
195 cmLog.debug('Trigger mentions hinting');
196 CodeMirror.showHint(cm, CodeMirror.hint.mentions, options);
197 }
198 }, 100);
199
200 // tell CodeMirror we didn't handle the key
201 // trick to trigger on a char but still complete it
202 return CodeMirror.Pass;
203 };
32 204
33 205 var initCodeMirror = function(textAreadId, resetUrl, focus, options) {
34 206 var ta = $('#' + textAreadId).get(0);
@@ -61,9 +233,6 b' var initCodeMirror = function(textAreadI'
61 233 var initCommentBoxCodeMirror = function(textAreaId, triggerActions){
62 234 var initialHeight = 100;
63 235
64 // global timer, used to cancel async loading
65 var loadUserHintTimer;
66
67 236 if (typeof userHintsCache === "undefined") {
68 237 userHintsCache = {};
69 238 cmLog.debug('Init empty cache for mentions');
@@ -72,96 +241,6 b' var initCommentBoxCodeMirror = function('
72 241 cmLog.debug('Element for textarea not found', textAreaId);
73 242 return;
74 243 }
75 var escapeRegExChars = function(value) {
76 return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
77 };
78 /**
79 * Load hints from external source returns an array of objects in a format
80 * that hinting lib requires
81 * @returns {Array}
82 */
83 var loadUserHints = function(query, triggerHints) {
84 cmLog.debug('Loading mentions users via AJAX');
85 var _users = [];
86 $.ajax({
87 type: 'GET',
88 data: {query: query},
89 url: pyroutes.url('user_autocomplete_data'),
90 headers: {'X-PARTIAL-XHR': true},
91 async: true
92 })
93 .done(function(data) {
94 var tmpl = '<img class="gravatar" src="{0}"/>{1}';
95 $.each(data.suggestions, function(i) {
96 var userObj = data.suggestions[i];
97
98 if (userObj.username !== "default") {
99 _users.push({
100 text: userObj.username + " ",
101 org_text: userObj.username,
102 displayText: userObj.value_display, // search that field
103 // internal caches
104 _icon_link: userObj.icon_link,
105 _text: userObj.value_display,
106
107 render: function(elt, data, completion) {
108 var el = document.createElement('div');
109 el.className = "CodeMirror-hint-entry";
110 el.innerHTML = tmpl.format(
111 completion._icon_link, completion._text);
112 elt.appendChild(el);
113 }
114 });
115 }
116 });
117 cmLog.debug('Mention users loaded');
118 // set to global cache
119 userHintsCache[query] = _users;
120 triggerHints(userHintsCache[query]);
121 })
122 .fail(function(data, textStatus, xhr) {
123 alert("error processing request: " + textStatus);
124 });
125 };
126
127 /**
128 * filters the results based on the current context
129 * @param users
130 * @param context
131 * @returns {Array}
132 */
133 var filterUsers = function(users, context) {
134 var MAX_LIMIT = 10;
135 var filtered_users = [];
136 var curWord = context.string;
137
138 cmLog.debug('Filtering users based on query:', curWord);
139 $.each(users, function(i) {
140 var match = users[i];
141 var searchText = match.displayText;
142
143 if (!curWord ||
144 searchText.toLowerCase().lastIndexOf(curWord) !== -1) {
145 // reset state
146 match._text = match.displayText;
147 if (curWord) {
148 // do highlighting
149 var pattern = '(' + escapeRegExChars(curWord) + ')';
150 match._text = searchText.replace(
151 new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
152 }
153
154 filtered_users.push(match);
155 }
156 // to not return to many results, use limit of filtered results
157 if (filtered_users.length > MAX_LIMIT) {
158 return false;
159 }
160 });
161
162 return filtered_users;
163 };
164
165 244 /**
166 245 * Filter action based on typed in text
167 246 * @param actions
@@ -200,25 +279,6 b' var initCommentBoxCodeMirror = function('
200 279 return filtered_actions;
201 280 };
202 281
203 var completeAfter = function(cm, pred) {
204 var options = {
205 completeSingle: false,
206 async: true,
207 closeOnUnfocus: true
208 };
209 var cur = cm.getCursor();
210 setTimeout(function() {
211 if (!cm.state.completionActive) {
212 cmLog.debug('Trigger mentions hinting');
213 CodeMirror.showHint(cm, CodeMirror.hint.mentions, options);
214 }
215 }, 100);
216
217 // tell CodeMirror we didn't handle the key
218 // trick to trigger on a char but still complete it
219 return CodeMirror.Pass;
220 };
221
222 282 var submitForm = function(cm, pred) {
223 283 $(cm.display.input.textarea.form).submit();
224 284 return CodeMirror.Pass;
@@ -238,7 +298,7 b' var initCommentBoxCodeMirror = function('
238 298 };
239 299
240 300 var extraKeys = {
241 "'@'": completeAfter,
301 "'@'": CodeMirrorCompleteAfter,
242 302 Tab: function(cm) {
243 303 // space indent instead of TABS
244 304 var spaces = new Array(cm.getOption("indentUnit") + 1).join(" ");
@@ -285,66 +345,6 b' var initCommentBoxCodeMirror = function('
285 345 self.setSize(null, height);
286 346 });
287 347
288 var mentionHint = function(editor, callback, options) {
289 var cur = editor.getCursor();
290 var curLine = editor.getLine(cur.line).slice(0, cur.ch);
291
292 // match on @ +1char
293 var tokenMatch = new RegExp(
294 '(^@| @)([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]*)$').exec(curLine);
295
296 var tokenStr = '';
297 if (tokenMatch !== null && tokenMatch.length > 0){
298 tokenStr = tokenMatch[0].strip();
299 } else {
300 // skip if we didn't match our token
301 return;
302 }
303
304 var context = {
305 start: (cur.ch - tokenStr.length) + 1,
306 end: cur.ch,
307 string: tokenStr.slice(1),
308 type: null
309 };
310
311 // case when we put the @sign in fron of a string,
312 // eg <@ we put it here>sometext then we need to prepend to text
313 if (context.end > cur.ch) {
314 context.start = context.start + 1; // we add to the @ sign
315 context.end = cur.ch; // don't eat front part just append
316 context.string = context.string.slice(1, cur.ch - context.start);
317 }
318
319 cmLog.debug('Mention context', context);
320
321 var triggerHints = function(userHints){
322 return callback({
323 list: filterUsers(userHints, context),
324 from: CodeMirror.Pos(cur.line, context.start),
325 to: CodeMirror.Pos(cur.line, context.end)
326 });
327 };
328
329 var queryBasedHintsCache = undefined;
330 // if we have something in the cache, try to fetch the query based cache
331 if (userHintsCache !== {}){
332 queryBasedHintsCache = userHintsCache[context.string];
333 }
334
335 if (queryBasedHintsCache !== undefined) {
336 cmLog.debug('Users loaded from cache');
337 triggerHints(queryBasedHintsCache);
338 } else {
339 // this takes care for async loading, and then displaying results
340 // and also propagates the userHintsCache
341 window.clearTimeout(loadUserHintTimer);
342 loadUserHintTimer = setTimeout(function() {
343 loadUserHints(context.string, triggerHints);
344 }, 300);
345 }
346 };
347
348 348 var actionHint = function(editor, options) {
349 349 var cur = editor.getCursor();
350 350 var curLine = editor.getLine(cur.line).slice(0, cur.ch);
@@ -408,7 +408,7 b' var initCommentBoxCodeMirror = function('
408 408 to: CodeMirror.Pos(cur.line, context.end)
409 409 };
410 410 };
411 CodeMirror.registerHelper("hint", "mentions", mentionHint);
411 CodeMirror.registerHelper("hint", "mentions", CodeMirrorMentionHint);
412 412 CodeMirror.registerHelper("hint", "actions", actionHint);
413 413 return cm;
414 414 };
General Comments 0
You need to be logged in to leave comments. Login now