##// END OF EJS Templates
code-mirror: implement slash actions instead of ctrl+space....
marcink -
r1361:9c1873b2 default
parent child Browse files
Show More
@@ -1,530 +1,582 b''
1 // # Copyright (C) 2010-2017 RhodeCode GmbH
1 // # Copyright (C) 2010-2017 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
5 // # (only), as published by the Free Software Foundation.
5 // # (only), as published by the Free Software Foundation.
6 // #
6 // #
7 // # This program is distributed in the hope that it will be useful,
7 // # This program is distributed in the hope that it will be useful,
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // # GNU General Public License for more details.
10 // # GNU General Public License for more details.
11 // #
11 // #
12 // # You should have received a copy of the GNU Affero General Public License
12 // # You should have received a copy of the GNU Affero General Public License
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 // #
14 // #
15 // # This program is dual-licensed. If you wish to learn more about the
15 // # This program is dual-licensed. If you wish to learn more about the
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 /**
19 /**
20 * Code Mirror
20 * Code Mirror
21 */
21 */
22 // global code-mirror logger;, to enable run
22 // global code-mirror logger;, to enable run
23 // Logger.get('CodeMirror').setLevel(Logger.DEBUG)
23 // Logger.get('CodeMirror').setLevel(Logger.DEBUG)
24
24
25 cmLog = Logger.get('CodeMirror');
25 cmLog = Logger.get('CodeMirror');
26 cmLog.setLevel(Logger.OFF);
26 cmLog.setLevel(Logger.OFF);
27
27
28
28
29 //global cache for inline forms
29 //global cache for inline forms
30 var userHintsCache = {};
30 var userHintsCache = {};
31
31
32 // global timer, used to cancel async loading
32 // global timer, used to cancel async loading
33 var CodeMirrorLoadUserHintTimer;
33 var CodeMirrorLoadUserHintTimer;
34
34
35 var escapeRegExChars = function(value) {
35 var escapeRegExChars = function(value) {
36 return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
36 return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
37 };
37 };
38
38
39 /**
39 /**
40 * Load hints from external source returns an array of objects in a format
40 * Load hints from external source returns an array of objects in a format
41 * that hinting lib requires
41 * that hinting lib requires
42 * @returns {Array}
42 * @returns {Array}
43 */
43 */
44 var CodeMirrorLoadUserHints = function(query, triggerHints) {
44 var CodeMirrorLoadUserHints = function(query, triggerHints) {
45 cmLog.debug('Loading mentions users via AJAX');
45 cmLog.debug('Loading mentions users via AJAX');
46 var _users = [];
46 var _users = [];
47 $.ajax({
47 $.ajax({
48 type: 'GET',
48 type: 'GET',
49 data: {query: query},
49 data: {query: query},
50 url: pyroutes.url('user_autocomplete_data'),
50 url: pyroutes.url('user_autocomplete_data'),
51 headers: {'X-PARTIAL-XHR': true},
51 headers: {'X-PARTIAL-XHR': true},
52 async: true
52 async: true
53 })
53 })
54 .done(function(data) {
54 .done(function(data) {
55 var tmpl = '<img class="gravatar" src="{0}"/>{1}';
55 var tmpl = '<img class="gravatar" src="{0}"/>{1}';
56 $.each(data.suggestions, function(i) {
56 $.each(data.suggestions, function(i) {
57 var userObj = data.suggestions[i];
57 var userObj = data.suggestions[i];
58
58
59 if (userObj.username !== "default") {
59 if (userObj.username !== "default") {
60 _users.push({
60 _users.push({
61 text: userObj.username + " ",
61 text: userObj.username + " ",
62 org_text: userObj.username,
62 org_text: userObj.username,
63 displayText: userObj.value_display, // search that field
63 displayText: userObj.value_display, // search that field
64 // internal caches
64 // internal caches
65 _icon_link: userObj.icon_link,
65 _icon_link: userObj.icon_link,
66 _text: userObj.value_display,
66 _text: userObj.value_display,
67
67
68 render: function(elt, data, completion) {
68 render: function(elt, data, completion) {
69 var el = document.createElement('div');
69 var el = document.createElement('div');
70 el.className = "CodeMirror-hint-entry";
70 el.className = "CodeMirror-hint-entry";
71 el.innerHTML = tmpl.format(
71 el.innerHTML = tmpl.format(
72 completion._icon_link, completion._text);
72 completion._icon_link, completion._text);
73 elt.appendChild(el);
73 elt.appendChild(el);
74 }
74 }
75 });
75 });
76 }
76 }
77 });
77 });
78 cmLog.debug('Mention users loaded');
78 cmLog.debug('Mention users loaded');
79 // set to global cache
79 // set to global cache
80 userHintsCache[query] = _users;
80 userHintsCache[query] = _users;
81 triggerHints(userHintsCache[query]);
81 triggerHints(userHintsCache[query]);
82 })
82 })
83 .fail(function(data, textStatus, xhr) {
83 .fail(function(data, textStatus, xhr) {
84 alert("error processing request: " + textStatus);
84 alert("error processing request: " + textStatus);
85 });
85 });
86 };
86 };
87
87
88 /**
88 /**
89 * filters the results based on the current context
89 * filters the results based on the current context
90 * @param users
90 * @param users
91 * @param context
91 * @param context
92 * @returns {Array}
92 * @returns {Array}
93 */
93 */
94 var CodeMirrorFilterUsers = function(users, context) {
94 var CodeMirrorFilterUsers = function(users, context) {
95 var MAX_LIMIT = 10;
95 var MAX_LIMIT = 10;
96 var filtered_users = [];
96 var filtered_users = [];
97 var curWord = context.string;
97 var curWord = context.string;
98
98
99 cmLog.debug('Filtering users based on query:', curWord);
99 cmLog.debug('Filtering users based on query:', curWord);
100 $.each(users, function(i) {
100 $.each(users, function(i) {
101 var match = users[i];
101 var match = users[i];
102 var searchText = match.displayText;
102 var searchText = match.displayText;
103
103
104 if (!curWord ||
104 if (!curWord ||
105 searchText.toLowerCase().lastIndexOf(curWord) !== -1) {
105 searchText.toLowerCase().lastIndexOf(curWord) !== -1) {
106 // reset state
106 // reset state
107 match._text = match.displayText;
107 match._text = match.displayText;
108 if (curWord) {
108 if (curWord) {
109 // do highlighting
109 // do highlighting
110 var pattern = '(' + escapeRegExChars(curWord) + ')';
110 var pattern = '(' + escapeRegExChars(curWord) + ')';
111 match._text = searchText.replace(
111 match._text = searchText.replace(
112 new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
112 new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
113 }
113 }
114
114
115 filtered_users.push(match);
115 filtered_users.push(match);
116 }
116 }
117 // to not return to many results, use limit of filtered results
117 // to not return to many results, use limit of filtered results
118 if (filtered_users.length > MAX_LIMIT) {
118 if (filtered_users.length > MAX_LIMIT) {
119 return false;
119 return false;
120 }
120 }
121 });
121 });
122
122
123 return filtered_users;
123 return filtered_users;
124 };
124 };
125
125
126 var CodeMirrorMentionHint = function(editor, callback, options) {
126 var CodeMirrorMentionHint = function(editor, callback, options) {
127 var cur = editor.getCursor();
127 var cur = editor.getCursor();
128 var curLine = editor.getLine(cur.line).slice(0, cur.ch);
128 var curLine = editor.getLine(cur.line).slice(0, cur.ch);
129
129
130 // match on @ +1char
130 // match on @ +1char
131 var tokenMatch = new RegExp(
131 var tokenMatch = new RegExp(
132 '(^@| @)([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]*)$').exec(curLine);
132 '(^@| @)([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]*)$').exec(curLine);
133
133
134 var tokenStr = '';
134 var tokenStr = '';
135 if (tokenMatch !== null && tokenMatch.length > 0){
135 if (tokenMatch !== null && tokenMatch.length > 0){
136 tokenStr = tokenMatch[0].strip();
136 tokenStr = tokenMatch[0].strip();
137 } else {
137 } else {
138 // skip if we didn't match our token
138 // skip if we didn't match our token
139 return;
139 return;
140 }
140 }
141
141
142 var context = {
142 var context = {
143 start: (cur.ch - tokenStr.length) + 1,
143 start: (cur.ch - tokenStr.length) + 1,
144 end: cur.ch,
144 end: cur.ch,
145 string: tokenStr.slice(1),
145 string: tokenStr.slice(1),
146 type: null
146 type: null
147 };
147 };
148
148
149 // case when we put the @sign in fron of a string,
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
150 // eg <@ we put it here>sometext then we need to prepend to text
151 if (context.end > cur.ch) {
151 if (context.end > cur.ch) {
152 context.start = context.start + 1; // we add to the @ sign
152 context.start = context.start + 1; // we add to the @ sign
153 context.end = cur.ch; // don't eat front part just append
153 context.end = cur.ch; // don't eat front part just append
154 context.string = context.string.slice(1, cur.ch - context.start);
154 context.string = context.string.slice(1, cur.ch - context.start);
155 }
155 }
156
156
157 cmLog.debug('Mention context', context);
157 cmLog.debug('Mention context', context);
158
158
159 var triggerHints = function(userHints){
159 var triggerHints = function(userHints){
160 return callback({
160 return callback({
161 list: CodeMirrorFilterUsers(userHints, context),
161 list: CodeMirrorFilterUsers(userHints, context),
162 from: CodeMirror.Pos(cur.line, context.start),
162 from: CodeMirror.Pos(cur.line, context.start),
163 to: CodeMirror.Pos(cur.line, context.end)
163 to: CodeMirror.Pos(cur.line, context.end)
164 });
164 });
165 };
165 };
166
166
167 var queryBasedHintsCache = undefined;
167 var queryBasedHintsCache = undefined;
168 // if we have something in the cache, try to fetch the query based cache
168 // if we have something in the cache, try to fetch the query based cache
169 if (userHintsCache !== {}){
169 if (userHintsCache !== {}){
170 queryBasedHintsCache = userHintsCache[context.string];
170 queryBasedHintsCache = userHintsCache[context.string];
171 }
171 }
172
172
173 if (queryBasedHintsCache !== undefined) {
173 if (queryBasedHintsCache !== undefined) {
174 cmLog.debug('Users loaded from cache');
174 cmLog.debug('Users loaded from cache');
175 triggerHints(queryBasedHintsCache);
175 triggerHints(queryBasedHintsCache);
176 } else {
176 } else {
177 // this takes care for async loading, and then displaying results
177 // this takes care for async loading, and then displaying results
178 // and also propagates the userHintsCache
178 // and also propagates the userHintsCache
179 window.clearTimeout(CodeMirrorLoadUserHintTimer);
179 window.clearTimeout(CodeMirrorLoadUserHintTimer);
180 CodeMirrorLoadUserHintTimer = setTimeout(function() {
180 CodeMirrorLoadUserHintTimer = setTimeout(function() {
181 CodeMirrorLoadUserHints(context.string, triggerHints);
181 CodeMirrorLoadUserHints(context.string, triggerHints);
182 }, 300);
182 }, 300);
183 }
183 }
184 };
184 };
185
185
186 var CodeMirrorCompleteAfter = function(cm, pred) {
186 var CodeMirrorCompleteAfter = function(cm, pred) {
187 var options = {
187 var options = {
188 completeSingle: false,
188 completeSingle: false,
189 async: true,
189 async: true,
190 closeOnUnfocus: true
190 closeOnUnfocus: true
191 };
191 };
192 var cur = cm.getCursor();
192 var cur = cm.getCursor();
193 setTimeout(function() {
193 setTimeout(function() {
194 if (!cm.state.completionActive) {
194 if (!cm.state.completionActive) {
195 cmLog.debug('Trigger mentions hinting');
195 cmLog.debug('Trigger mentions hinting');
196 CodeMirror.showHint(cm, CodeMirror.hint.mentions, options);
196 CodeMirror.showHint(cm, CodeMirror.hint.mentions, options);
197 }
197 }
198 }, 100);
198 }, 100);
199
199
200 // tell CodeMirror we didn't handle the key
200 // tell CodeMirror we didn't handle the key
201 // trick to trigger on a char but still complete it
201 // trick to trigger on a char but still complete it
202 return CodeMirror.Pass;
202 return CodeMirror.Pass;
203 };
203 };
204
204
205 var initCodeMirror = function(textAreadId, resetUrl, focus, options) {
205 var initCodeMirror = function(textAreadId, resetUrl, focus, options) {
206 var ta = $('#' + textAreadId).get(0);
206 var ta = $('#' + textAreadId).get(0);
207 if (focus === undefined) {
207 if (focus === undefined) {
208 focus = true;
208 focus = true;
209 }
209 }
210
210
211 // default options
211 // default options
212 var codeMirrorOptions = {
212 var codeMirrorOptions = {
213 mode: "null",
213 mode: "null",
214 lineNumbers: true,
214 lineNumbers: true,
215 indentUnit: 4,
215 indentUnit: 4,
216 autofocus: focus
216 autofocus: focus
217 };
217 };
218
218
219 if (options !== undefined) {
219 if (options !== undefined) {
220 // extend with custom options
220 // extend with custom options
221 codeMirrorOptions = $.extend(true, codeMirrorOptions, options);
221 codeMirrorOptions = $.extend(true, codeMirrorOptions, options);
222 }
222 }
223
223
224 var myCodeMirror = CodeMirror.fromTextArea(ta, codeMirrorOptions);
224 var myCodeMirror = CodeMirror.fromTextArea(ta, codeMirrorOptions);
225
225
226 $('#reset').on('click', function(e) {
226 $('#reset').on('click', function(e) {
227 window.location = resetUrl;
227 window.location = resetUrl;
228 });
228 });
229
229
230 return myCodeMirror;
230 return myCodeMirror;
231 };
231 };
232
232
233 var initCommentBoxCodeMirror = function(textAreaId, triggerActions){
233 var initCommentBoxCodeMirror = function(textAreaId, triggerActions){
234 var initialHeight = 100;
234 var initialHeight = 100;
235
235
236 if (typeof userHintsCache === "undefined") {
236 if (typeof userHintsCache === "undefined") {
237 userHintsCache = {};
237 userHintsCache = {};
238 cmLog.debug('Init empty cache for mentions');
238 cmLog.debug('Init empty cache for mentions');
239 }
239 }
240 if (!$(textAreaId).get(0)) {
240 if (!$(textAreaId).get(0)) {
241 cmLog.debug('Element for textarea not found', textAreaId);
241 cmLog.debug('Element for textarea not found', textAreaId);
242 return;
242 return;
243 }
243 }
244 /**
244 /**
245 * Filter action based on typed in text
245 * Filter action based on typed in text
246 * @param actions
246 * @param actions
247 * @param context
247 * @param context
248 * @returns {Array}
248 * @returns {Array}
249 */
249 */
250
250
251 var filterActions = function(actions, context){
251 var filterActions = function(actions, context){
252
252 var MAX_LIMIT = 10;
253 var MAX_LIMIT = 10;
253 var filtered_actions= [];
254 var filtered_actions = [];
254 var curWord = context.string;
255 var curWord = context.string;
255
256
256 cmLog.debug('Filtering actions based on query:', curWord);
257 cmLog.debug('Filtering actions based on query:', curWord);
257 $.each(actions, function(i) {
258 $.each(actions, function(i) {
258 var match = actions[i];
259 var match = actions[i];
259 var searchText = match.displayText;
260 var searchText = match.searchText;
260
261
261 if (!curWord ||
262 if (!curWord ||
262 searchText.toLowerCase().lastIndexOf(curWord) !== -1) {
263 searchText.toLowerCase().lastIndexOf(curWord) !== -1) {
263 // reset state
264 // reset state
264 match._text = match.displayText;
265 match._text = match.displayText;
265 if (curWord) {
266 if (curWord) {
266 // do highlighting
267 // do highlighting
267 var pattern = '(' + escapeRegExChars(curWord) + ')';
268 var pattern = '(' + escapeRegExChars(curWord) + ')';
268 match._text = searchText.replace(
269 match._text = searchText.replace(
269 new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
270 new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
270 }
271 }
271
272
272 filtered_actions.push(match);
273 filtered_actions.push(match);
273 }
274 }
274 // to not return to many results, use limit of filtered results
275 // to not return to many results, use limit of filtered results
275 if (filtered_actions.length > MAX_LIMIT) {
276 if (filtered_actions.length > MAX_LIMIT) {
276 return false;
277 return false;
277 }
278 }
278 });
279 });
280
279 return filtered_actions;
281 return filtered_actions;
280 };
282 };
281
283
282 var submitForm = function(cm, pred) {
284 var submitForm = function(cm, pred) {
283 $(cm.display.input.textarea.form).submit();
285 $(cm.display.input.textarea.form).submit();
284 return CodeMirror.Pass;
286 return CodeMirror.Pass;
285 };
287 };
286
288
287 var completeActions = function(cm, pred) {
289 var completeActions = function(actions){
288 var cur = cm.getCursor();
290
289 var options = {
291 return function(cm, pred) {
290 closeOnUnfocus: true
292 var cur = cm.getCursor();
291 };
293 var options = {
292 setTimeout(function() {
294 closeOnUnfocus: true
293 if (!cm.state.completionActive) {
295 };
294 cmLog.debug('Trigger actions hinting');
296 setTimeout(function() {
295 CodeMirror.showHint(cm, CodeMirror.hint.actions, options);
297 if (!cm.state.completionActive) {
298 cmLog.debug('Trigger actions hinting');
299 CodeMirror.showHint(cm, CodeMirror.hint.actions, options);
300 }
301 }, 100);
302
303 // tell CodeMirror we didn't handle the key
304 // trick to trigger on a char but still complete it
305 return CodeMirror.Pass;
296 }
306 }
297 }, 100);
298 };
307 };
299
308
300 var extraKeys = {
309 var extraKeys = {
301 "'@'": CodeMirrorCompleteAfter,
310 "'@'": CodeMirrorCompleteAfter,
302 Tab: function(cm) {
311 Tab: function(cm) {
303 // space indent instead of TABS
312 // space indent instead of TABS
304 var spaces = new Array(cm.getOption("indentUnit") + 1).join(" ");
313 var spaces = new Array(cm.getOption("indentUnit") + 1).join(" ");
305 cm.replaceSelection(spaces);
314 cm.replaceSelection(spaces);
306 }
315 }
307 };
316 };
308 // submit form on Meta-Enter
317 // submit form on Meta-Enter
309 if (OSType === "mac") {
318 if (OSType === "mac") {
310 extraKeys["Cmd-Enter"] = submitForm;
319 extraKeys["Cmd-Enter"] = submitForm;
311 }
320 }
312 else {
321 else {
313 extraKeys["Ctrl-Enter"] = submitForm;
322 extraKeys["Ctrl-Enter"] = submitForm;
314 }
323 }
315
324
316 if (triggerActions) {
325 if (triggerActions) {
317 extraKeys["Ctrl-Space"] = completeActions;
326 // register triggerActions for this instance
327 extraKeys["'/'"] = completeActions(triggerActions);
318 }
328 }
319
329
320 var cm = CodeMirror.fromTextArea($(textAreaId).get(0), {
330 var cm = CodeMirror.fromTextArea($(textAreaId).get(0), {
321 lineNumbers: false,
331 lineNumbers: false,
322 indentUnit: 4,
332 indentUnit: 4,
323 viewportMargin: 30,
333 viewportMargin: 30,
324 // this is a trick to trigger some logic behind codemirror placeholder
334 // this is a trick to trigger some logic behind codemirror placeholder
325 // it influences styling and behaviour.
335 // it influences styling and behaviour.
326 placeholder: " ",
336 placeholder: " ",
327 extraKeys: extraKeys,
337 extraKeys: extraKeys,
328 lineWrapping: true
338 lineWrapping: true
329 });
339 });
330
340
331 cm.setSize(null, initialHeight);
341 cm.setSize(null, initialHeight);
332 cm.setOption("mode", DEFAULT_RENDERER);
342 cm.setOption("mode", DEFAULT_RENDERER);
333 CodeMirror.autoLoadMode(cm, DEFAULT_RENDERER); // load rst or markdown mode
343 CodeMirror.autoLoadMode(cm, DEFAULT_RENDERER); // load rst or markdown mode
334 cmLog.debug('Loading codemirror mode', DEFAULT_RENDERER);
344 cmLog.debug('Loading codemirror mode', DEFAULT_RENDERER);
335 // start listening on changes to make auto-expanded editor
345 // start listening on changes to make auto-expanded editor
336 cm.on("change", function(self) {
346 cm.on("change", function(self) {
337 var height = initialHeight;
347 var height = initialHeight;
338 var lines = self.lineCount();
348 var lines = self.lineCount();
339 if ( lines > 6 && lines < 20) {
349 if ( lines > 6 && lines < 20) {
340 height = "auto";
350 height = "auto";
341 }
351 }
342 else if (lines >= 20){
352 else if (lines >= 20){
343 zheight = 20*15;
353 zheight = 20*15;
344 }
354 }
345 self.setSize(null, height);
355 self.setSize(null, height);
346 });
356 });
347
357
348 var actionHint = function(editor, options) {
358 var actionHint = function(editor, options) {
349 var cur = editor.getCursor();
359 var cur = editor.getCursor();
350 var curLine = editor.getLine(cur.line).slice(0, cur.ch);
360 var curLine = editor.getLine(cur.line).slice(0, cur.ch);
351
361
352 var tokenMatch = new RegExp('[a-zA-Z]{1}[a-zA-Z]*$').exec(curLine);
362 // match only on /+1 character minimum
363 var tokenMatch = new RegExp('(^/\|/\)([a-zA-Z]*)$').exec(curLine);
353
364
354 var tokenStr = '';
365 var tokenStr = '';
355 if (tokenMatch !== null && tokenMatch.length > 0){
366 if (tokenMatch !== null && tokenMatch.length > 0){
356 tokenStr = tokenMatch[0].strip();
367 tokenStr = tokenMatch[2].strip();
357 }
368 }
358
369
359 var context = {
370 var context = {
360 start: cur.ch - tokenStr.length,
371 start: (cur.ch - tokenStr.length) - 1,
361 end: cur.ch,
372 end: cur.ch,
362 string: tokenStr,
373 string: tokenStr,
363 type: null
374 type: null
364 };
375 };
365
376
366 var actions = [
377 var actions = [
367 {
378 {
368 text: "approve",
379 text: "approve",
380 searchText: "status approved",
369 displayText: _gettext('Set status to Approved'),
381 displayText: _gettext('Set status to Approved'),
370 hint: function(CodeMirror, data, completion) {
382 hint: function(CodeMirror, data, completion) {
371 CodeMirror.replaceRange("", completion.from || data.from,
383 CodeMirror.replaceRange("", completion.from || data.from,
372 completion.to || data.to, "complete");
384 completion.to || data.to, "complete");
373 $('#change_status_general').select2("val", 'approved').trigger('change');
385 $('#change_status_general').select2("val", 'approved').trigger('change');
374 },
386 },
375 render: function(elt, data, completion) {
387 render: function(elt, data, completion) {
376 var el = document.createElement('div');
388 var el = document.createElement('div');
377 el.className = "flag_status flag_status_comment_box approved pull-left";
389 el.className = "flag_status flag_status_comment_box approved pull-left";
378 elt.appendChild(el);
390 elt.appendChild(el);
379
391
380 el = document.createElement('span');
392 el = document.createElement('span');
381 el.innerHTML = completion.displayText;
393 el.innerHTML = completion.displayText;
382 elt.appendChild(el);
394 elt.appendChild(el);
383 }
395 }
384 },
396 },
385 {
397 {
386 text: "reject",
398 text: "reject",
399 searchText: "status rejected",
387 displayText: _gettext('Set status to Rejected'),
400 displayText: _gettext('Set status to Rejected'),
388 hint: function(CodeMirror, data, completion) {
401 hint: function(CodeMirror, data, completion) {
389 CodeMirror.replaceRange("", completion.from || data.from,
402 CodeMirror.replaceRange("", completion.from || data.from,
390 completion.to || data.to, "complete");
403 completion.to || data.to, "complete");
391 $('#change_status_general').select2("val", 'rejected').trigger('change');
404 $('#change_status_general').select2("val", 'rejected').trigger('change');
392 },
405 },
393 render: function(elt, data, completion) {
406 render: function(elt, data, completion) {
394 var el = document.createElement('div');
407 var el = document.createElement('div');
395 el.className = "flag_status flag_status_comment_box rejected pull-left";
408 el.className = "flag_status flag_status_comment_box rejected pull-left";
396 elt.appendChild(el);
409 elt.appendChild(el);
397
410
398 el = document.createElement('span');
411 el = document.createElement('span');
399 el.innerHTML = completion.displayText;
412 el.innerHTML = completion.displayText;
400 elt.appendChild(el);
413 elt.appendChild(el);
401 }
414 }
415 },
416 {
417 text: "as_todo",
418 searchText: "todo comment",
419 displayText: _gettext('TODO comment'),
420 hint: function(CodeMirror, data, completion) {
421 CodeMirror.replaceRange("", completion.from || data.from,
422 completion.to || data.to, "complete");
423 $('#comment_type_general').val('todo')
424 },
425 render: function(elt, data, completion) {
426 var el = document.createElement('div');
427 el.className = "pull-left";
428 elt.appendChild(el);
429
430 el = document.createElement('span');
431 el.innerHTML = completion.displayText;
432 elt.appendChild(el);
433 }
434 },
435 {
436 text: "as_note",
437 searchText: "note comment",
438 displayText: _gettext('Note Comment'),
439 hint: function(CodeMirror, data, completion) {
440 CodeMirror.replaceRange("", completion.from || data.from,
441 completion.to || data.to, "complete");
442 $('#comment_type_general').val('note')
443 },
444 render: function(elt, data, completion) {
445 var el = document.createElement('div');
446 el.className = "pull-left";
447 elt.appendChild(el);
448
449 el = document.createElement('span');
450 el.innerHTML = completion.displayText;
451 elt.appendChild(el);
452 }
402 }
453 }
403 ];
454 ];
404
455
405 return {
456 return {
406 list: filterActions(actions, context),
457 list: filterActions(actions, context),
407 from: CodeMirror.Pos(cur.line, context.start),
458 from: CodeMirror.Pos(cur.line, context.start),
408 to: CodeMirror.Pos(cur.line, context.end)
459 to: CodeMirror.Pos(cur.line, context.end)
409 };
460 };
461
410 };
462 };
411 CodeMirror.registerHelper("hint", "mentions", CodeMirrorMentionHint);
463 CodeMirror.registerHelper("hint", "mentions", CodeMirrorMentionHint);
412 CodeMirror.registerHelper("hint", "actions", actionHint);
464 CodeMirror.registerHelper("hint", "actions", actionHint);
413 return cm;
465 return cm;
414 };
466 };
415
467
416 var setCodeMirrorMode = function(codeMirrorInstance, mode) {
468 var setCodeMirrorMode = function(codeMirrorInstance, mode) {
417 CodeMirror.autoLoadMode(codeMirrorInstance, mode);
469 CodeMirror.autoLoadMode(codeMirrorInstance, mode);
418 codeMirrorInstance.setOption("mode", mode);
470 codeMirrorInstance.setOption("mode", mode);
419 };
471 };
420
472
421 var setCodeMirrorLineWrap = function(codeMirrorInstance, line_wrap) {
473 var setCodeMirrorLineWrap = function(codeMirrorInstance, line_wrap) {
422 codeMirrorInstance.setOption("lineWrapping", line_wrap);
474 codeMirrorInstance.setOption("lineWrapping", line_wrap);
423 };
475 };
424
476
425 var setCodeMirrorModeFromSelect = function(
477 var setCodeMirrorModeFromSelect = function(
426 targetSelect, targetFileInput, codeMirrorInstance, callback){
478 targetSelect, targetFileInput, codeMirrorInstance, callback){
427
479
428 $(targetSelect).on('change', function(e) {
480 $(targetSelect).on('change', function(e) {
429 cmLog.debug('codemirror select2 mode change event !');
481 cmLog.debug('codemirror select2 mode change event !');
430 var selected = e.currentTarget;
482 var selected = e.currentTarget;
431 var node = selected.options[selected.selectedIndex];
483 var node = selected.options[selected.selectedIndex];
432 var mimetype = node.value;
484 var mimetype = node.value;
433 cmLog.debug('picked mimetype', mimetype);
485 cmLog.debug('picked mimetype', mimetype);
434 var new_mode = $(node).attr('mode');
486 var new_mode = $(node).attr('mode');
435 setCodeMirrorMode(codeMirrorInstance, new_mode);
487 setCodeMirrorMode(codeMirrorInstance, new_mode);
436 cmLog.debug('set new mode', new_mode);
488 cmLog.debug('set new mode', new_mode);
437
489
438 //propose filename from picked mode
490 //propose filename from picked mode
439 cmLog.debug('setting mimetype', mimetype);
491 cmLog.debug('setting mimetype', mimetype);
440 var proposed_ext = getExtFromMimeType(mimetype);
492 var proposed_ext = getExtFromMimeType(mimetype);
441 cmLog.debug('file input', $(targetFileInput).val());
493 cmLog.debug('file input', $(targetFileInput).val());
442 var file_data = getFilenameAndExt($(targetFileInput).val());
494 var file_data = getFilenameAndExt($(targetFileInput).val());
443 var filename = file_data.filename || 'filename1';
495 var filename = file_data.filename || 'filename1';
444 $(targetFileInput).val(filename + proposed_ext);
496 $(targetFileInput).val(filename + proposed_ext);
445 cmLog.debug('proposed file', filename + proposed_ext);
497 cmLog.debug('proposed file', filename + proposed_ext);
446
498
447
499
448 if (typeof(callback) === 'function') {
500 if (typeof(callback) === 'function') {
449 try {
501 try {
450 cmLog.debug('running callback', callback);
502 cmLog.debug('running callback', callback);
451 callback(filename, mimetype, new_mode);
503 callback(filename, mimetype, new_mode);
452 } catch (err) {
504 } catch (err) {
453 console.log('failed to run callback', callback, err);
505 console.log('failed to run callback', callback, err);
454 }
506 }
455 }
507 }
456 cmLog.debug('finish iteration...');
508 cmLog.debug('finish iteration...');
457 });
509 });
458 };
510 };
459
511
460 var setCodeMirrorModeFromInput = function(
512 var setCodeMirrorModeFromInput = function(
461 targetSelect, targetFileInput, codeMirrorInstance, callback) {
513 targetSelect, targetFileInput, codeMirrorInstance, callback) {
462
514
463 // on type the new filename set mode
515 // on type the new filename set mode
464 $(targetFileInput).on('keyup', function(e) {
516 $(targetFileInput).on('keyup', function(e) {
465 var file_data = getFilenameAndExt(this.value);
517 var file_data = getFilenameAndExt(this.value);
466 if (file_data.ext === null) {
518 if (file_data.ext === null) {
467 return;
519 return;
468 }
520 }
469
521
470 var mimetypes = getMimeTypeFromExt(file_data.ext, true);
522 var mimetypes = getMimeTypeFromExt(file_data.ext, true);
471 cmLog.debug('mimetype from file', file_data, mimetypes);
523 cmLog.debug('mimetype from file', file_data, mimetypes);
472 var detected_mode;
524 var detected_mode;
473 var detected_option;
525 var detected_option;
474 for (var i in mimetypes) {
526 for (var i in mimetypes) {
475 var mt = mimetypes[i];
527 var mt = mimetypes[i];
476 if (!detected_mode) {
528 if (!detected_mode) {
477 detected_mode = detectCodeMirrorMode(this.value, mt);
529 detected_mode = detectCodeMirrorMode(this.value, mt);
478 }
530 }
479
531
480 if (!detected_option) {
532 if (!detected_option) {
481 cmLog.debug('#mimetype option[value="{0}"]'.format(mt));
533 cmLog.debug('#mimetype option[value="{0}"]'.format(mt));
482 if ($(targetSelect).find('option[value="{0}"]'.format(mt)).length) {
534 if ($(targetSelect).find('option[value="{0}"]'.format(mt)).length) {
483 detected_option = mt;
535 detected_option = mt;
484 }
536 }
485 }
537 }
486 }
538 }
487
539
488 cmLog.debug('detected mode', detected_mode);
540 cmLog.debug('detected mode', detected_mode);
489 cmLog.debug('detected option', detected_option);
541 cmLog.debug('detected option', detected_option);
490 if (detected_mode && detected_option){
542 if (detected_mode && detected_option){
491
543
492 $(targetSelect).select2("val", detected_option);
544 $(targetSelect).select2("val", detected_option);
493 setCodeMirrorMode(codeMirrorInstance, detected_mode);
545 setCodeMirrorMode(codeMirrorInstance, detected_mode);
494
546
495 if(typeof(callback) === 'function'){
547 if(typeof(callback) === 'function'){
496 try{
548 try{
497 cmLog.debug('running callback', callback);
549 cmLog.debug('running callback', callback);
498 var filename = file_data.filename + "." + file_data.ext;
550 var filename = file_data.filename + "." + file_data.ext;
499 callback(filename, detected_option, detected_mode);
551 callback(filename, detected_option, detected_mode);
500 }catch (err){
552 }catch (err){
501 console.log('failed to run callback', callback, err);
553 console.log('failed to run callback', callback, err);
502 }
554 }
503 }
555 }
504 }
556 }
505
557
506 });
558 });
507 };
559 };
508
560
509 var fillCodeMirrorOptions = function(targetSelect) {
561 var fillCodeMirrorOptions = function(targetSelect) {
510 //inject new modes, based on codeMirrors modeInfo object
562 //inject new modes, based on codeMirrors modeInfo object
511 var modes_select = $(targetSelect);
563 var modes_select = $(targetSelect);
512 for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
564 for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
513 var m = CodeMirror.modeInfo[i];
565 var m = CodeMirror.modeInfo[i];
514 var opt = new Option(m.name, m.mime);
566 var opt = new Option(m.name, m.mime);
515 $(opt).attr('mode', m.mode);
567 $(opt).attr('mode', m.mode);
516 modes_select.append(opt);
568 modes_select.append(opt);
517 }
569 }
518 };
570 };
519
571
520 var CodeMirrorPreviewEnable = function(edit_mode) {
572 var CodeMirrorPreviewEnable = function(edit_mode) {
521 // in case it a preview enabled mode enable the button
573 // in case it a preview enabled mode enable the button
522 if (['markdown', 'rst', 'gfm'].indexOf(edit_mode) !== -1) {
574 if (['markdown', 'rst', 'gfm'].indexOf(edit_mode) !== -1) {
523 $('#render_preview').removeClass('hidden');
575 $('#render_preview').removeClass('hidden');
524 }
576 }
525 else {
577 else {
526 if (!$('#render_preview').hasClass('hidden')) {
578 if (!$('#render_preview').hasClass('hidden')) {
527 $('#render_preview').addClass('hidden');
579 $('#render_preview').addClass('hidden');
528 }
580 }
529 }
581 }
530 };
582 };
General Comments 0
You need to be logged in to leave comments. Login now