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