|
1 /** |
|
2 * Functions dedicated to edition. |
|
3 * |
|
4 * :organization: Logilab |
|
5 * :copyright: 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
|
6 * :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
|
7 * |
|
8 */ |
|
9 |
|
10 //============= Eproperty form functions =====================================// |
|
11 /** |
|
12 * .. function:: setPropValueWidget(varname, tabindex) |
|
13 * |
|
14 * called on CWProperty key selection: |
|
15 * - get the selected value |
|
16 * - get a widget according to the key by a sync query to the server |
|
17 * - fill associated div with the returned html |
|
18 * |
|
19 * * `varname`, the name of the variable as used in the original creation form |
|
20 * * `tabindex`, the tabindex that should be set on the widget |
|
21 */ |
|
22 |
|
23 function setPropValueWidget(varname, tabindex) { |
|
24 var key = firstSelected(document.getElementById('pkey-subject:' + varname)); |
|
25 if (key) { |
|
26 var args = { |
|
27 fname: 'prop_widget', |
|
28 pageid: pageid, |
|
29 arg: $.map([key.value, varname, tabindex], JSON.stringify) |
|
30 }; |
|
31 cw.jqNode('div:value-subject:' + varname).loadxhtml(AJAX_BASE_URL, args, 'post'); |
|
32 } |
|
33 } |
|
34 |
|
35 // *** EDITION FUNCTIONS ****************************************** // |
|
36 /** |
|
37 * .. function:: reorderTabindex(start, formid) |
|
38 * |
|
39 * this function is called when an AJAX form was generated to |
|
40 * make sure tabindex remains consistent |
|
41 */ |
|
42 function reorderTabindex(start, formid) { |
|
43 var form = cw.getNode(formid || 'entityForm'); |
|
44 var inputTypes = ['INPUT', 'SELECT', 'TEXTAREA']; |
|
45 var tabindex = (start == null) ? 15: start; |
|
46 cw.utils.nodeWalkDepthFirst(form, function(elem) { |
|
47 var tagName = elem.tagName.toUpperCase(); |
|
48 if (jQuery.inArray(tagName, inputTypes) != -1) { |
|
49 if (jQuery(elem).attr('tabindex') != null) { |
|
50 tabindex += 1; |
|
51 jQuery(elem).attr('tabindex', tabindex); |
|
52 } |
|
53 return null; |
|
54 } |
|
55 return jQuery.grep(elem.childNodes, isElementNode); |
|
56 }); |
|
57 } |
|
58 |
|
59 function showMatchingSelect(selectedValue, eid) { |
|
60 if (selectedValue) { |
|
61 var divId = 'div' + selectedValue + '_' + eid; |
|
62 var divNode = jQuery('#' + divId); |
|
63 if (!divNode.length) { |
|
64 var args = { |
|
65 vid: 'unrelateddivs', |
|
66 relation: selectedValue, |
|
67 rql: rql_for_eid(eid), |
|
68 '__notemplate': 1 |
|
69 }; |
|
70 var d = jQuery('#unrelatedDivs_' + eid).loadxhtml(BASE_URL + 'view', args, 'post', 'append'); |
|
71 d.addCallback(function() { |
|
72 _showMatchingSelect(eid, jQuery('#' + divId)); |
|
73 }); |
|
74 } else { |
|
75 _showMatchingSelect(eid, divNode); |
|
76 } |
|
77 } else { |
|
78 _showMatchingSelect(eid, null); |
|
79 } |
|
80 } |
|
81 |
|
82 /** |
|
83 * .. function:: _showMatchingSelect(eid, divNode) |
|
84 * |
|
85 * * `divNode`, a jQuery selection |
|
86 */ |
|
87 function _showMatchingSelect(eid, divNode) { |
|
88 // hide all divs, and then show the matching one |
|
89 // (would actually be better to directly hide the displayed one) |
|
90 jQuery('#unrelatedDivs_' + eid).children().hide(); |
|
91 // divNode not found means 'no relation selected' (i.e. first blank item) |
|
92 if (divNode && divNode.length) { |
|
93 divNode.show(); |
|
94 } |
|
95 } |
|
96 |
|
97 /** |
|
98 * .. function:: buildPendingInsertHandle(elementId, element_name, selectNodeId, eid) |
|
99 * |
|
100 * this function builds a Handle to cancel pending insertion |
|
101 */ |
|
102 function buildPendingInsertHandle(elementId, element_name, selectNodeId, eid) { |
|
103 jscall = "javascript: cancelPendingInsert('" + [elementId, element_name, selectNodeId, eid].join("', '") + "')"; |
|
104 return A({ |
|
105 'class': 'handle', |
|
106 'href': jscall, |
|
107 'title': _("cancel this insert") |
|
108 }, |
|
109 '[x]'); |
|
110 } |
|
111 |
|
112 function buildEntityLine(relationName, selectedOptionNode, comboId, eid) { |
|
113 // textContent doesn't seem to work on selectedOptionNode |
|
114 var content = selectedOptionNode.firstChild.nodeValue; |
|
115 var handle = buildPendingInsertHandle(selectedOptionNode.id, 'tr', comboId, eid); |
|
116 var link = A({ |
|
117 'href': 'view?rql=' + selectedOptionNode.value, |
|
118 'class': 'editionPending', |
|
119 'id': 'a' + selectedOptionNode.id |
|
120 }, |
|
121 content); |
|
122 var tr = TR({ |
|
123 'id': 'tr' + selectedOptionNode.id |
|
124 }, |
|
125 [TH(null, relationName), TD(null, [handle, link])]); |
|
126 try { |
|
127 var separator = cw.getNode('relationSelectorRow_' + eid); |
|
128 //dump('relationSelectorRow_' + eid) XXX warn dump is not implemented in konqueror (at least) |
|
129 // XXX Warning: separator.parentNode is not (always ?) the |
|
130 // table itself, but an intermediate node (TableSectionElement) |
|
131 var tableBody = separator.parentNode; |
|
132 tableBody.insertBefore(tr, separator); |
|
133 } catch(ex) { |
|
134 log("got exception(2)!" + ex); |
|
135 } |
|
136 } |
|
137 |
|
138 function buildEntityCell(relationName, selectedOptionNode, comboId, eid) { |
|
139 var handle = buildPendingInsertHandle(selectedOptionNode.id, 'div_insert_', comboId, eid); |
|
140 var link = A({ |
|
141 'href': 'view?rql=' + selectedOptionNode.value, |
|
142 'class': 'editionPending', |
|
143 'id': 'a' + selectedOptionNode.id |
|
144 }, |
|
145 content); |
|
146 var div = DIV({ |
|
147 'id': 'div_insert_' + selectedOptionNode.id |
|
148 }, |
|
149 [handle, link]); |
|
150 try { |
|
151 var td = jQuery('#cell' + relationName + '_' + eid); |
|
152 td.appendChild(div); |
|
153 } catch(ex) { |
|
154 alert("got exception(3)!" + ex); |
|
155 } |
|
156 } |
|
157 |
|
158 function addPendingInsert(optionNode, eid, cell, relname) { |
|
159 var value = jQuery(optionNode).attr('value'); |
|
160 if (!value) { |
|
161 // occurs when the first element in the box is selected (which is not |
|
162 // an entity but the combobox title) |
|
163 return; |
|
164 } |
|
165 // 2nd special case |
|
166 if (value.indexOf('http') == 0) { |
|
167 document.location = value; |
|
168 return; |
|
169 } |
|
170 // add hidden parameter |
|
171 var entityForm = jQuery('#entityForm'); |
|
172 var oid = optionNode.id.substring(2); // option id is prefixed by "id" |
|
173 loadRemote(AJAX_BASE_URL, ajaxFuncArgs('add_pending_inserts', null, |
|
174 [oid.split(':')]), 'POST', true); |
|
175 var selectNode = optionNode.parentNode; |
|
176 // remove option node |
|
177 selectNode.removeChild(optionNode); |
|
178 // add line in table |
|
179 if (cell) { |
|
180 // new relation as a cell in multiple edit |
|
181 // var relation_name = relationSelected.getAttribute('value'); |
|
182 // relation_name = relation_name.slice(0, relation_name.lastIndexOf('_')); |
|
183 buildEntityCell(relname, optionNode, selectNode.id, eid); |
|
184 } |
|
185 else { |
|
186 var relationSelector = cw.getNode('relationSelector_' + eid); |
|
187 var relationSelected = relationSelector.options[relationSelector.selectedIndex]; |
|
188 // new relation as a line in simple edit |
|
189 buildEntityLine(relationSelected.text, optionNode, selectNode.id, eid); |
|
190 } |
|
191 } |
|
192 |
|
193 function cancelPendingInsert(elementId, element_name, comboId, eid) { |
|
194 // remove matching insert element |
|
195 var entityView = cw.jqNode('a' + elementId).text(); |
|
196 cw.jqNode(element_name + elementId).remove(); |
|
197 if (comboId) { |
|
198 // re-insert option in combobox if it was taken from there |
|
199 var selectNode = cw.getNode(comboId); |
|
200 // XXX what on object relation |
|
201 if (selectNode) { |
|
202 var options = selectNode.options; |
|
203 var node_id = elementId.substring(0, elementId.indexOf(':')); |
|
204 options[options.length] = OPTION({ |
|
205 'id': elementId, |
|
206 'value': node_id |
|
207 }, |
|
208 entityView); |
|
209 } |
|
210 } |
|
211 elementId = elementId.substring(2, elementId.length); |
|
212 loadRemote(AJAX_BASE_URL, ajaxFuncArgs('remove_pending_insert', null, |
|
213 elementId.split(':')), 'GET', true); |
|
214 } |
|
215 |
|
216 /** |
|
217 * .. function:: buildPendingDeleteHandle(elementId, eid) |
|
218 * |
|
219 * this function builds a Handle to cancel pending insertion |
|
220 */ |
|
221 function buildPendingDeleteHandle(elementId, eid) { |
|
222 var jscall = "javascript: addPendingDelete('" + elementId + ', ' + eid + "');"; |
|
223 return A({ |
|
224 'href': jscall, |
|
225 'class': 'pendingDeleteHandle', |
|
226 'title': _("delete this relation") |
|
227 }, |
|
228 '[x]'); |
|
229 } |
|
230 |
|
231 /** |
|
232 * .. function:: addPendingDelete(nodeId, eid) |
|
233 * |
|
234 * * `nodeId`, eid_from:r_type:eid_to |
|
235 */ |
|
236 function addPendingDelete(nodeId, eid) { |
|
237 var d = loadRemote(AJAX_BASE_URL, ajaxFuncArgs('add_pending_delete', null, nodeId.split(':'))); |
|
238 d.addCallback(function() { |
|
239 // and strike entity view |
|
240 cw.jqNode('span' + nodeId).addClass('pendingDelete'); |
|
241 // replace handle text |
|
242 cw.jqNode('handle' + nodeId).text('+'); |
|
243 }); |
|
244 } |
|
245 |
|
246 /** |
|
247 * .. function:: cancelPendingDelete(nodeId, eid) |
|
248 * |
|
249 * * `nodeId`, eid_from:r_type:eid_to |
|
250 */ |
|
251 function cancelPendingDelete(nodeId, eid) { |
|
252 var d = loadRemote(AJAX_BASE_URL, ajaxFuncArgs('remove_pending_delete', null, nodeId.split(':'))); |
|
253 d.addCallback(function() { |
|
254 // reset link's CSS class |
|
255 cw.jqNode('span' + nodeId).removeClass('pendingDelete'); |
|
256 // replace handle text |
|
257 cw.jqNode('handle' + nodeId).text('x'); |
|
258 }); |
|
259 } |
|
260 |
|
261 /** |
|
262 * .. function:: togglePendingDelete(nodeId, eid) |
|
263 * |
|
264 * * `nodeId`, eid_from:r_type:eid_to |
|
265 */ |
|
266 function togglePendingDelete(nodeId, eid) { |
|
267 // node found means we should cancel deletion |
|
268 if (jQuery(cw.getNode('span' + nodeId)).hasClass('pendingDelete')) { |
|
269 cancelPendingDelete(nodeId, eid); |
|
270 } else { |
|
271 addPendingDelete(nodeId, eid); |
|
272 } |
|
273 } |
|
274 |
|
275 function selectForAssociation(tripletIdsString, originalEid) { |
|
276 var tripletlist = $.map(tripletIdsString.split('-'), |
|
277 function(x) { return [x.split(':')] ;}); |
|
278 var d = loadRemote(AJAX_BASE_URL, ajaxFuncArgs('add_pending_inserts', null, tripletlist)); |
|
279 d.addCallback(function() { |
|
280 var args = { |
|
281 vid: 'edition', |
|
282 __mode: 'normal', |
|
283 rql: rql_for_eid(originalEid) |
|
284 }; |
|
285 document.location = 'view?' + asURL(args); |
|
286 }); |
|
287 |
|
288 } |
|
289 |
|
290 function updateInlinedEntitiesCounters(rtype, role) { |
|
291 jQuery('div.inline-' + rtype + '-' + role + '-slot span.icounter').each(function(i) { |
|
292 this.innerHTML = i + 1; |
|
293 }); |
|
294 } |
|
295 |
|
296 /** |
|
297 * .. function:: addInlineCreationForm(peid, petype, ttype, rtype, role, i18nctx, insertBefore) |
|
298 * |
|
299 * makes an AJAX request to get an inline-creation view's content |
|
300 * * `peid`, the parent entity eid |
|
301 * |
|
302 * * `petype`, the parent entity type |
|
303 * |
|
304 * * `ttype`, the target (inlined) entity type |
|
305 * |
|
306 * * `rtype`, the relation type between both entities |
|
307 */ |
|
308 function addInlineCreationForm(peid, petype, ttype, rtype, role, i18nctx, insertBefore) { |
|
309 insertBefore = insertBefore || cw.getNode('add' + rtype + ':' + peid + 'link').parentNode; |
|
310 var args = ajaxFuncArgs('inline_creation_form', null, peid, petype, ttype, rtype, role, i18nctx); |
|
311 var d = loadRemote(AJAX_BASE_URL, args); |
|
312 d.addCallback(function(response) { |
|
313 var dom = getDomFromResponse(response); |
|
314 loadAjaxHtmlHead(dom); |
|
315 var form = jQuery(dom); |
|
316 form.css('display', 'none'); |
|
317 form.insertBefore(insertBefore).slideDown('fast'); |
|
318 updateInlinedEntitiesCounters(rtype, role); |
|
319 reorderTabindex(null, $(insertBefore).closest('form')[0]); |
|
320 jQuery(cw).trigger('inlinedform-added', form); |
|
321 // if the inlined form contains a file input, we must force |
|
322 // the form enctype to multipart/form-data |
|
323 if (form.find('input:file').length) { |
|
324 // NOTE: IE doesn't support dynamic enctype modification, we have |
|
325 // to set encoding too. |
|
326 form.closest('form').attr('enctype', 'multipart/form-data').attr('encoding', 'multipart/form-data'); |
|
327 } |
|
328 _postAjaxLoad(dom); |
|
329 }); |
|
330 d.addErrback(function(xxx) { |
|
331 cw.log('xxx =', xxx); |
|
332 }); |
|
333 } |
|
334 |
|
335 /** |
|
336 * .. function:: removeInlineForm(peid, rtype, role, eid, showaddnewlink) |
|
337 * |
|
338 * removes the part of the form used to edit an inlined entity |
|
339 */ |
|
340 function removeInlineForm(peid, rtype, role, eid, showaddnewlink) { |
|
341 cw.jqNode(['div', peid, rtype, eid].join('-')).slideUp('fast', function() { |
|
342 $(this).remove(); |
|
343 updateInlinedEntitiesCounters(rtype, role); |
|
344 }); |
|
345 if (showaddnewlink) { |
|
346 toggleVisibility(showaddnewlink); |
|
347 } |
|
348 } |
|
349 |
|
350 /** |
|
351 * .. function:: removeInlinedEntity(peid, rtype, eid) |
|
352 * |
|
353 * alternatively adds or removes the hidden input that make the |
|
354 * edition of the relation `rtype` possible between `peid` and `eid` |
|
355 * * `peid`, the parent entity eid |
|
356 * |
|
357 * * `rtype`, the relation type between both entities |
|
358 * |
|
359 * * `eid`, the inlined entity eid |
|
360 */ |
|
361 function removeInlinedEntity(peid, rtype, eid) { |
|
362 // XXX work around the eid_param thing (eid + ':' + eid) for #471746 |
|
363 var nodeid = ['rel', peid, rtype, eid + ':' + eid].join('-'); |
|
364 var node = cw.jqNode(nodeid); |
|
365 if (!node.attr('cubicweb:type')) { |
|
366 node.attr('cubicweb:type', node.val()); |
|
367 node.val(''); |
|
368 var divid = ['div', peid, rtype, eid].join('-'); |
|
369 cw.jqNode(divid).fadeTo('fast', 0.5); |
|
370 var noticeid = ['notice', peid, rtype, eid].join('-'); |
|
371 cw.jqNode(noticeid).fadeIn('fast'); |
|
372 } |
|
373 } |
|
374 |
|
375 function restoreInlinedEntity(peid, rtype, eid) { |
|
376 // XXX work around the eid_param thing (eid + ':' + eid) for #471746 |
|
377 var nodeid = ['rel', peid, rtype, eid + ':' + eid].join('-'); |
|
378 var node = cw.jqNode(nodeid); |
|
379 if (node.attr('cubicweb:type')) { |
|
380 node.val(node.attr('cubicweb:type')); |
|
381 node.attr('cubicweb:type', ''); |
|
382 cw.jqNode(['fs', peid, rtype, eid].join('-')).append(node); |
|
383 var divid = ['div', peid, rtype, eid].join('-'); |
|
384 cw.jqNode(divid).fadeTo('fast', 1); |
|
385 var noticeid = ['notice', peid, rtype, eid].join('-'); |
|
386 cw.jqNode(noticeid).hide(); |
|
387 } |
|
388 } |
|
389 |
|
390 function _clearPreviousErrors(formid) { |
|
391 // on some case (eg max request size exceeded, we don't know the formid |
|
392 if (formid) { |
|
393 jQuery('#' + formid + 'ErrorMessage').remove(); |
|
394 jQuery('#' + formid + ' span.errorMsg').remove(); |
|
395 jQuery('#' + formid + ' .error').removeClass('error'); |
|
396 } else { |
|
397 jQuery('span.errorMsg').remove(); |
|
398 jQuery('.error').removeClass('error'); |
|
399 } |
|
400 } |
|
401 |
|
402 function _displayValidationerrors(formid, eid, errors) { |
|
403 var globalerrors = []; |
|
404 var firsterrfield = null; |
|
405 for (fieldname in errors) { |
|
406 var errmsg = errors[fieldname]; |
|
407 if (!fieldname) { |
|
408 globalerrors.push(errmsg); |
|
409 } else { |
|
410 var fieldid = fieldname + ':' + eid; |
|
411 var suffixes = ['', '-subject', '-object']; |
|
412 var found = false; |
|
413 // XXX remove suffixes at some point |
|
414 for (var i = 0, length = suffixes.length; i < length; i++) { |
|
415 var field = cw.jqNode(fieldname + suffixes[i] + ':' + eid); |
|
416 if (field && jQuery(field).attr('type') != 'hidden') { |
|
417 if (!firsterrfield) { |
|
418 firsterrfield = 'err-' + fieldid; |
|
419 } |
|
420 jQuery(field).addClass('error'); |
|
421 var span = SPAN({ |
|
422 'id': 'err-' + fieldid, |
|
423 'class': "errorMsg" |
|
424 }, |
|
425 errmsg); |
|
426 field.before(span); |
|
427 found = true; |
|
428 break; |
|
429 } |
|
430 } |
|
431 if (!found) { |
|
432 firsterrfield = formid; |
|
433 globalerrors.push(_(fieldname) + ' : ' + errmsg); |
|
434 } |
|
435 } |
|
436 } |
|
437 if (globalerrors.length) { |
|
438 if (globalerrors.length == 1) { |
|
439 var innernode = SPAN(null, globalerrors[0]); |
|
440 } else { |
|
441 var linodes =[]; |
|
442 for(var i=0; i<globalerrors.length; i++){ |
|
443 linodes.push(LI(null, globalerrors[i])); |
|
444 } |
|
445 var innernode = UL(null, linodes); |
|
446 } |
|
447 // insert DIV and innernode before the form |
|
448 var div = DIV({ |
|
449 'class': "errorMessage", |
|
450 'id': formid + 'ErrorMessage' |
|
451 }); |
|
452 div.appendChild(innernode); |
|
453 jQuery('#' + formid).before(div); |
|
454 } |
|
455 return firsterrfield || formid; |
|
456 } |
|
457 |
|
458 function handleFormValidationResponse(formid, onsuccess, onfailure, result, cbargs) { |
|
459 // Success |
|
460 if (result[0]) { |
|
461 if (onsuccess) { |
|
462 onsuccess(result, formid, cbargs); |
|
463 } else { |
|
464 document.location.href = result[1]; |
|
465 } |
|
466 return true; |
|
467 } |
|
468 if (onfailure && ! onfailure(result, formid, cbargs)) { |
|
469 return false; |
|
470 } |
|
471 unfreezeFormButtons(formid); |
|
472 // Failures |
|
473 _clearPreviousErrors(formid); |
|
474 var descr = result[1]; |
|
475 var errmsg; |
|
476 // Unknown structure |
|
477 if ( !cw.utils.isArrayLike(descr) || descr.length != 2 ) { |
|
478 errmsg = descr; |
|
479 } else { |
|
480 _displayValidationerrors(formid, descr[0], descr[1]); |
|
481 errmsg = _("please correct errors below"); |
|
482 } |
|
483 updateMessage(errmsg); |
|
484 // ensure the browser does not scroll down |
|
485 document.location.hash = '#header'; |
|
486 return false; |
|
487 } |
|
488 |
|
489 /** |
|
490 * .. function:: unfreezeFormButtons(formid) |
|
491 * |
|
492 * unfreeze form buttons when the validation process is over |
|
493 */ |
|
494 function unfreezeFormButtons(formid) { |
|
495 jQuery('#progress').hide(); |
|
496 // on some case (eg max request size exceeded, we don't know the formid |
|
497 if (formid) { |
|
498 jQuery('#' + formid + ' .validateButton').removeAttr('disabled'); |
|
499 } else { |
|
500 jQuery('.validateButton').removeAttr('disabled'); |
|
501 } |
|
502 return true; |
|
503 } |
|
504 |
|
505 /** |
|
506 * .. function:: freezeFormButtons(formid) |
|
507 * |
|
508 * disable form buttons while the validation is being done |
|
509 */ |
|
510 function freezeFormButtons(formid) { |
|
511 jQuery('#progress').show(); |
|
512 jQuery('#' + formid + ' .validateButton').attr('disabled', 'disabled'); |
|
513 return true; |
|
514 } |
|
515 |
|
516 /** |
|
517 * .. function:: postForm(bname, bvalue, formid) |
|
518 * |
|
519 * used by additional submit buttons to remember which button was clicked |
|
520 */ |
|
521 function postForm(bname, bvalue, formid) { |
|
522 var form = cw.getNode(formid); |
|
523 if (bname) { |
|
524 var child = form.appendChild(INPUT({ |
|
525 type: 'hidden', |
|
526 name: bname, |
|
527 value: bvalue |
|
528 })); |
|
529 } |
|
530 var onsubmit = form.onsubmit; |
|
531 if (!onsubmit || (onsubmit && onsubmit())) { |
|
532 form.submit(); |
|
533 } |
|
534 if (bname) { |
|
535 jQuery(child).remove(); |
|
536 } |
|
537 } |
|
538 |
|
539 /** |
|
540 * Cancel the operations done on the given form. |
|
541 * |
|
542 */ |
|
543 $(function () { |
|
544 $(document).on('click', '.cwjs-edition-cancel', function (evt) { |
|
545 var $mynode = $(evt.currentTarget), |
|
546 $form = $mynode.closest('form'), |
|
547 $error = $form.find(':input[name="__errorurl"]'), |
|
548 errorurl = $error.attr('value'), |
|
549 args = ajaxFuncArgs('cancel_edition', null, errorurl); |
|
550 loadRemote(AJAX_BASE_URL, args, 'POST', true); |
|
551 history.back(); |
|
552 return false; |
|
553 }); |
|
554 }); |
|
555 |
|
556 |
|
557 /** |
|
558 * .. function:: validateForm(formid, action, onsuccess, onfailure) |
|
559 * |
|
560 * called on traditionnal form submission : the idea is to try |
|
561 * to post the form. If the post is successful, `validateForm` redirects |
|
562 * to the appropriate URL. Otherwise, the validation errors are displayed |
|
563 * around the corresponding input fields. |
|
564 */ |
|
565 function validateForm(formid, action, onsuccess, onfailure) { |
|
566 freezeFormButtons(formid); |
|
567 try { |
|
568 var zipped = cw.utils.formContents(formid); |
|
569 var args = ajaxFuncArgs('validate_form', null, action, zipped[0], zipped[1]); |
|
570 var d = loadRemote(AJAX_BASE_URL, args, 'POST'); |
|
571 } catch(ex) { |
|
572 cw.log('got exception', ex); |
|
573 return false; |
|
574 } |
|
575 d.addCallback(function(result, req) { |
|
576 handleFormValidationResponse(formid, onsuccess, onfailure, result); |
|
577 }); |
|
578 return false; |
|
579 } |