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