ccef02a08a3608b479660f663b851db652bddcc2
[mailer.git] / js / ajax-common.js
1 /**
2  * Common JavaScript functions for AJAX requests (they are general purpose).
3  * --------------------------------------------------------------------
4  * $Revision::                                                        $
5  * $Date::                                                            $
6  * $Tag:: 0.2.1-FINAL                                                 $
7  * $Author::                                                          $
8  * --------------------------------------------------------------------
9  * Copyright (c) 2003 - 2009 by Roland Haeder
10  * Copyright (c) 2009 - 2012 by Mailer Developer Team
11  * For more information visit: http://mxchange.org
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
26  * MA  02110-1301  USA
27  */
28
29 // Init variables
30 var currentTabId       = null;
31 var defaultTabId       = null;
32 var footerElements     = new Array();
33 var changedElements    = new Array();
34 var formChanged        = false;
35 var saveChangesId      = null;
36 var lastErrorMessage   = null;
37 var saveChangesPending = false;
38
39 // Add all footer navigation elements
40 footerElements[0] = 'next';
41 footerElements[1] = 'previous';
42
43 // Setter for current tab id
44 function setCurrentTabId (tabId) {
45         // Set it
46         //* DEBUG: */ alert('currentTabId=' + currentTabId + ',tabId=' + tabId);
47         currentTabId = tabId;
48 }
49
50 // Checks whether a given element is visible by checking 'display: none'
51 function isElementVisible (prefix, element) {
52         // Get element
53         var el = document.getElementById(prefix + '_' + element);
54
55         // Is element set?
56         if (el == null || el == undefined) {
57                 throw new '"' + prefix + '_' + element + '" does not exist.';
58         }
59
60         // Default is visible
61         var isVisible = ((el.style.display == undefined) || ((el.style.display != 'none') && (el.style.display != '')));
62
63         // Return status
64         return isVisible;
65 }
66
67 // Marks a tab navigation entry
68 function markTabNavigation (prefix, tab) {
69         // Is process working?
70         if (isElementVisible(prefix, 'process')) {
71                 // Then exit silently
72                 return;
73         } // END - if
74
75         // Get all li-tags
76         var li = document.getElementsByTagName('li');
77
78         // Set current tab name
79         var currentTabName = 'tab_enabled';
80
81         // Set tab name
82         var tabName = prefix + '_' + tab;
83
84         // Search for all menus
85         for (var i = 0; i < li.length; i++) {
86                 // Debug message
87                 //* DEBUG: */ document.write('className=' + li[i].className + ',prefix=' + prefix + ',tab=' + tab + '<br />');
88
89                 // Check for valid tab
90                 if (li[i].className.substr(0, currentTabName.length) == currentTabName) {
91                         // Okay does match (don't change any others)
92                         if (li[i].id == tabName) {
93                                 // Mark this menu
94                                 $('li#' + tabName).addClass('tab_active');
95                                 //li[i].className += ' tab_active';
96                         } else {
97                                 // Unmark this menu
98                                 $('li#' + li[i].id).removeClass('tab_active');
99                                 //li[i].className = currentTabName;
100                         }
101                 } // END - if
102         } // END - for
103 }
104
105 // Enables a given element
106 function enableElement (element) {
107         // Remove class and attribute
108         $(element).removeClass('disabled');
109         $(element).removeAttr('disabled');
110 }
111
112 // Disables a given element
113 function disableElement (element) {
114         // Add class and attribute
115         $(element).addClass('disabled');
116         $(element).attr('disabled', 'disabled');
117
118         // Blur the element
119         $(element).blur();
120 }
121
122 // Enables a given footer navigation element
123 function enableFooterNavigationPage (element) {
124         // Remove the 'disabled' class and attribute
125         if (element == 'finish') {
126                 enableElement('input#finish');
127         } else {
128                 enableElement('input#' + element + '_page');
129                 disableElement('input#finish');
130         }
131 }
132
133 // Resets footer navigation by adding CSS class 'disabled'
134 function resetFooterNavigation () {
135         // Remove the 'disabled' class and attribute
136         for (var i = 0; i < footerElements.length; i++) {
137                 $('input#' + footerElements[i] + '_page').addClass('disabled');
138                 $('input#' + footerElements[i] + '_page').attr('disabled', 'disabled');
139                 $('input#' + footerElements[i] + '_page').blur();
140         } // END - for
141 }
142
143 // Getter for AJAX content
144 function getAjaxContent () {
145         // Is it defined?
146         if ($('body').data('ajax_content') == undefined) {
147                 // Not set
148                 throw new 'ajax_content requested but not set.';
149         } // END - if
150
151         // Return it
152         return $('body').data('ajax_content');
153 }
154
155 // "Setter" for AJAX content but does decode the content
156 function setAjaxDecodedContent (ajax_content) {
157         // Decode URL-encoded data ...
158         var decoded = decodeUrlEncoding(ajax_content);
159
160         // ... and set it
161         setAjaxContent(decoded);
162 }
163
164 // Setter for AJAX content
165 function setAjaxContent (ajax_content) {
166         $('body').data('ajax_content', ajax_content);
167 }
168
169 // Getter for AJAX success
170 function getAjaxSuccess () {
171         return $('body').data('ajax_success');
172 }
173
174 // Setter for AJAX success
175 function setAjaxSuccess (success) {
176         $('body').data('ajax_success', success);
177 }
178
179 // Set AJAX reply and decode JSON if requested
180 function setAjaxReply (reply, isJson) {
181         // Is it JSON URL-encoded content?
182         if ((isJson != undefined) && (isJson == true)) {
183                 // Decode URL-encoding (for some reason it must be here ...)
184                 var localReply = decodeUrlEncoding(reply);
185
186                 // Then decode it, replace '%20' with space before because '%20' breakes JSON content
187                 var obj = jQuery.parseJSON(localReply.replace('%20', ' '));
188
189                 // ... and set it
190                 setAjaxContent(obj);
191         } else {
192                 // Handle the content over to decode it
193                 setAjaxDecodedContent(reply);
194         }
195 }
196
197 // Sends out an AJAX request
198 function sendAjaxRequest (level, doValue, extra, isJson) {
199         // By default all requests failed
200         setAjaxSuccess(false);
201
202         // Reset AJAX content
203         setAjaxContent('');
204
205         // Send out the raw request
206         $.ajax({
207                 url: 'ajax.php',
208                 type: 'POST',
209                 data: 'level=' + level + '&do=' + doValue + extra,
210                 dataType: 'json',
211                 async: false,
212
213                 // Called on success
214                 success: function (ajax_content) {
215                         // Is ajax_content set?
216                         if (ajax_content.reply_content == undefined) {
217                                 // This shall not happen
218                                 throw new 'ajax_content.reply_content not returned from ajax.php, please fix your scripts. (1)';
219                         } else if (ajax_content.reply_content == null) {
220                                 // This shall not happen, too
221                                 throw new 'ajax_content.reply_content=null from ajax.php, please fix your scripts. (2)';
222                         }
223
224                         // Set AJAX reply
225                         setAjaxReply(ajax_content.reply_content, isJson);
226
227                         // Mark it as success
228                         setAjaxSuccess(true);
229                 },
230
231                 // Called in case of an error (e.g. HTTP response status not '200 OK')
232                 error: function (ajax_content) {
233                         // Is ajax_content set?
234                         if (ajax_content.reply_content == undefined) {
235                                 // Is 'responseText' there?
236                                 if (ajax_content.responseText != undefined) {
237                                         // Then parse it
238                                         var obj = jQuery.parseJSON(ajax_content.responseText.replace('%20', ' '));
239
240                                         // Is 'reply_content' set?
241                                         if (obj.reply_content == undefined) {
242                                                 // This shall not happen
243                                                 throw new 'obj.reply_content not returned from ajax.php, please fix your scripts. (3)';
244                                         } // END - if
245
246                                         // Set it
247                                         setAjaxReply(obj.reply_content, false);
248                                         return false;
249                                 } else {
250                                         // This shall not happen
251                                         throw new 'ajax_content.reply_content not returned from ajax.php, please fix your scripts. (4)';
252                                 }
253                         } else if (ajax_content.reply_content == null) {
254                                 // This shall not happen, too
255                                 throw new 'ajax_content.reply_content=null from ajax.php, please fix your scripts. (5)';
256                         }
257
258                         // Set AJAX reply
259                         setAjaxReply(ajax_content.reply_content, isJson);
260                 }
261         });
262
263         // Return status
264         return getAjaxSuccess();
265 }
266
267 // Enables footer navigation buttons
268 function enableFooterNavigation (prefix, tabId) {
269         // Is process working?
270         if (isElementVisible(prefix, 'process')) {
271                 // Then exit silently
272                 return;
273         } // END - if
274
275         // Reset both footer navigation first
276         resetFooterNavigation();
277
278         // Do the AJAX request (JSON as content is enabled)
279         if (sendAjaxRequest(prefix, 'footer_navigation', '&tab=' + tabId, true) == true) {
280                 // Parse the content
281                 $.each(getAjaxContent(), function (i, value) {
282                         // Enable current element
283                         enableFooterNavigationPage(value);
284                 });
285         } else {
286                 // Display error window
287                 displayErrorWindow(prefix, getAjaxContent());
288         }
289 }
290
291 // Requests an AJAX content
292 function requestAjaxContent (prefix, htmlId, tabId, footerNavigation) {
293         // Is process working?
294         if (isElementVisible(prefix, 'process')) {
295                 // Then exit silently
296                 return;
297         } // END - if
298
299         // Check if this request is disabled
300         if ($('#' + prefix + '_' + tabId).hasClass('tab_disabled')) {
301                 // Clicked on a disabled tabId so blur it
302                 //* DEBUG: */ alert('requestAjaxContent(): prefix=' + prefix + ',htmlId=' + htmlId + ',tabId=' + tabId + ' - DISABLED!');
303                 return;
304         } else if (formChanged == true) {
305                 // Has changed form , so output message to browser
306                 //* DEBUG: */ alert('requestAjaxContent(): prefix=' + prefix + ',htmlId=' + htmlId + ',tabId=' + tabId + ' - FORM CHANGED!');
307                 displayChangedWarningWindow(prefix, tabId);
308
309                 // Abort here
310                 return;
311         }
312
313         // Only request if content is different
314         //* DEBUG: */ alert('requestAjaxContent(): prefix=' + prefix + ',htmlId=' + htmlId + ',tabId=' + tabId + ',currentTabId=' + currentTabId);
315         if (tabId != currentTabId) {
316                 // Set tabId as current
317                 //* DEBUG: */ alert('requestAjaxContent(): prefix=' + prefix + ',htmlId=' + htmlId + ',tabId=' + tabId + ' - Calling setCurrentTabId()');
318                 setCurrentTabId(tabId);
319
320                 // Fade the old content out
321                 $('#' + htmlId).fadeOut('fast', function() {
322                         // Send AJAX request
323                         if (sendAjaxRequest(prefix, 'request_content', '&tab=' + tabId, false) == true) {
324                                 // Add the HTML content
325                                 $('#' + htmlId).html(getAjaxContent());
326
327                                 // Fade the content in
328                                 $('#' + htmlId).fadeIn('fast', function() {
329                                         // This differs, so mark the menu and request content
330                                         markTabNavigation(prefix, tabId);
331
332                                         // Is the footer navigation enabled?
333                                         if (footerNavigation == true) {
334                                                 // Change footer navigation as well
335                                                 enableFooterNavigation(prefix, tabId);
336                                         } // END - if
337                                 });
338                         } else {
339                                 // Display error window
340                                 displayErrorWindow(prefix, getAjaxContent());
341                         }
342                 });
343         } else {
344                 // Same id
345                 //* DEBUG: */ alert('SAME!');
346         }
347 }
348
349 // Displays a test window
350 function displayTestWindow (prefix, element) {
351         // Register click-event for error window
352         $('#' + prefix + '_error_close').click(function () {
353                 // Close the window
354                 closeErrorWindow(prefix);
355         });
356
357         // Register click-event for warning window
358         $('#' + prefix + '_warning_close').click(function () {
359                 // Close the window
360                 //* DEBUG: */ alert('displayTestWindow(): prefix=' + prefix + ' - calling closeWarningWindow()');
361                 closeWarningWindow(prefix);
362         });
363
364         // Request it from the AJAX backend
365         if (sendAjaxRequest(prefix, 'test', '', false) == true) {
366                 // Transfer the returned content to the prefix_warning_content id
367                 $('#' + prefix + '_warning_content').html(getAjaxContent());
368
369                 // Fade the warning in
370                 $('#' + prefix + '_warning').fadeIn('slow', function() {
371                         // Enable element
372                         enableElement(element);
373                 });
374         } else {
375                 // Display error message
376                 displayErrorWindow(prefix, getAjaxContent());
377         }
378 }
379
380 // Displays a warning window above the form to warn about changed&unsafed fields
381 function displayChangedWarningWindow (prefix, button) {
382         // Fade out warning window, if open
383         //* DEBUG: */ alert('displayChangedWarningWindow(): prefix=' + prefix + ',button=' + button + ' - calling closeWarningWindow()');
384         closeWarningWindow(prefix);
385
386         // Fade error out for eye-candy, if open
387         closeErrorWindow(prefix);
388
389         // Fade it out for eye-candy
390         closeProcessWindow(prefix);
391
392         // Abort here if warningDisplayed is still true
393         if (isElementVisible(prefix, 'warning')) {
394                 // Make sure this doesn't happen
395                 return;
396         } // END - if
397
398         // Request it from the AJAX backend
399         if (sendAjaxRequest(prefix, 'change_warning', '&button=' + button + '&elements=' + changedElements.join(':')) == true) {
400                 // Transfer the returned content to the prefix_warning_content id
401                 $('#' + prefix + '_warning_content').html(getAjaxContent());
402
403                 // Fade the warning in
404                 $('#' + prefix + '_warning').fadeIn('slow', function() {
405                         // Do nothing for now
406                 });
407         } else {
408                 // Display error message
409                 displayErrorWindow(prefix, getAjaxContent());
410         }
411 }
412
413 // Displays the error window for given prefix and content
414 function displayErrorWindow (prefix, ajax_content) {
415         // Fade out warning window, if open
416         //* DEBUG: */ alert('displayErrorWindow(): prefix=' + prefix + ' - calling closeWarningWindow()');
417         closeWarningWindow(prefix);
418
419         // Fade it out for eye-candy
420         closeErrorWindow(prefix);
421
422         // Fade it out for eye-candy
423         closeProcessWindow(prefix);
424
425         // Abort here if errorDisplayed is still true
426         if (isElementVisible(prefix, 'error')) {
427                 // Make sure this doesn't happen
428                 return;
429         } // END - if
430
431         // Copy the response text to the error variable
432         if (ajax_content.reply_content != undefined) {
433                 $('#' + prefix + '_error_content').html(ajax_content.reply_content);
434         } else {
435                 $('#' + prefix + '_error_content').html(ajax_content);
436         }
437
438         // Fade the error in
439         $('#' + prefix + '_error').fadeIn('slow', function() {
440                 // Do nothing for now
441         });
442 }
443
444 // Displays the process window for given prefix and content
445 function displayProcessWindow (prefix, ajax_content) {
446         // Fade out warning window, if open
447         //* DEBUG: */ alert('displayProcessWindow(): prefix=' + prefix + ' - calling closeWarningWindow()');
448         closeWarningWindow(prefix);
449
450         // Fade it out for eye-candy
451         closeErrorWindow(prefix);
452
453         // Fade it out for eye-candy
454         closeProcessWindow(prefix);
455
456         // Abort here if processDisplayed is still true
457         if (isElementVisible(prefix, 'process')) {
458                 // Make sure this doesn't happen
459                 return;
460         } // END - if
461
462         // Copy the response text to the process variable
463         if (ajax_content.reply_content != undefined) {
464                 // Set HTML content
465                 setProcessContent(prefix, ajax_content.reply_content);
466         } else {
467                 setProcessContent(prefix, ajax_content);
468         }
469
470         // Fade the process in
471         $('#' + prefix + '_process').fadeIn('slow', function() {
472                 // Do nothing for now
473         });
474 }
475
476 // Sets "process content"
477 function setProcessContent (prefix, content) {
478         // Set HTML content
479         $('#' + prefix + '_process_content').html(content);
480 }
481
482 // Waits until the window has been closed
483 function closeErrorLocked () {
484         // Has all been loaded?
485         if (!isElementVisible(prefix, 'error')) {
486                 // Then release ready()
487                 $.holdReady(false);
488         } else {
489                 // Recursive call again
490                 window.setTimeout('closeErrorLocked()', 10);
491         }
492 }
493
494 // Waits until the window has been closed
495 function closeProcessLocked () {
496         // Has all been loaded?
497         if (!isElementVisible(prefix, 'process')) {
498                 // Then release ready()
499                 $.holdReady(false);
500         } else {
501                 // Recursive call again
502                 window.setTimeout('closeProcessLocked()', 10);
503         }
504 }
505
506 // Closes an error window
507 function closeErrorWindow (prefix, waitClose, resetCurrentTabId) {
508         // Is the error displayed?
509         if (isElementVisible(prefix, 'error')) {
510                 // Shall we wait ("sync") until the animation has completed?
511                 if (waitClose == true) {
512                         // Hold the ready status
513                         $.holdReady(true);
514                 } // END - if
515
516                 // Yes, then fade it out
517                 $('#' + prefix + '_error').fadeOut('fast', function() {
518                         // Set current tab id to default
519                         if (resetCurrentTabId == true) {
520                                 setCurrentTabId(defaultTabId);
521                         } // END - if
522                 });
523
524                 // Shall this animation be "synchronized"?
525                 if (waitClose == true) {
526                         // Wait for the window has been closed
527                         closeErrorLocked();
528                 } // END - if
529         } // END - if
530 }
531
532 // Closes an process window
533 function closeProcessWindow (prefix, waitClose, resetCurrentTabId) {
534         // Is the process displayed?
535         if (isElementVisible(prefix, 'process')) {
536                 // Shall we wait ("sync") until the animation has completed?
537                 if (waitClose == true) {
538                         // Hold the ready status
539                         $.holdReady(true);
540                 } // END - if
541
542                 // Yes, then fade it out
543                 $('#' + prefix + '_process').fadeOut('fast', function() {
544                         // Set current tab id to default
545                         if (resetCurrentTabId == true) {
546                                 setCurrentTabId(defaultTabId);
547                         } // END - if
548                 });
549
550                 // Shall this animation be "synchronized"?
551                 if (waitClose == true) {
552                         // Wait for the window has been closed
553                         closeProcessLocked();
554                 } // END - if
555         } // END - if
556 }
557
558 // Waits until the window has been closed
559 function closeWarningLocked () {
560         // Has all been loaded?
561         if (!isElementVisible(prefix, 'warning')) {
562                 // Then release ready()
563                 $.holdReady(false);
564         } else {
565                 // Recursive call again
566                 window.setTimeout('closeWarningLocked()', 10);
567         }
568 }
569
570 // Closes an warning window
571 function closeWarningWindow (prefix, waitClose, resetCurrentTabId) {
572         //* DEBUG: */ alert('prefix=' + prefix + ',waitClose=' + waitClose + ' - ENTERED!');
573         // Is the warning displayed?
574         if (isElementVisible(prefix, 'warning')) {
575                 // Shall we wait ("sync") until the animation has completed?
576                 //* DEBUG: */ alert('prefix=' + prefix + ',waitClose=' + waitClose + ',warningDisplayed=true');
577                 if (waitClose == true) {
578                         // Hold the ready status
579                         $.holdReady(true);
580                 } // END - if
581
582                 // Yes, then fade it out
583                 $('#' + prefix + '_warning').fadeOut('fast', function() {
584                         // Set current tab id to default
585                         if (resetCurrentTabId == true) {
586                                 setCurrentTabId(defaultTabId);
587                         } // END - if
588                 });
589
590                 // Shall this animation be "synchronized"?
591                 if (waitClose == true) {
592                         // Wait for the window has been closed
593                         //* DEBUG: */ alert('prefix=' + prefix + ',waitClose=' + waitClose + ' - LOCKED!');
594                         closeWarningLocked();
595                 } // END - if
596         } // END - if
597 }
598
599 // A footer navigation button has been clicked
600 function doFooterPage (prefix, htmlId, button) {
601         //* DEBUG: */ alert('doFooterPage(): prefix=' + prefix + ',htmlId=' + htmlId + ',button=' + button + ' - ENTERED!');
602         // Has something being changed?
603         if (formChanged == true) {
604                 // Output message to browser
605                 displayChangedWarningWindow(prefix, button);
606
607                 // Abort here
608                 return;
609         } // END - if
610
611         // Is process working?
612         if (isElementVisible(prefix, 'process')) {
613                 // Then exit silently
614                 return;
615         } // END - if
616
617         // Is there a 'next' entry?
618         //* DEBUG: */ alert('doFooterPage(): button=' + button + ',currentTabId=' + currentTabId + ',nextPage[currentTabId]=' + nextPage[currentTabId]);
619         if ((button == 'next') && (nextPage[currentTabId] != null)) {
620                 // Then call the AJAX requester
621                 var page = nextPage[currentTabId];
622         } else if ((button == 'previous') && (previousPage[currentTabId] != null)) {
623                 // Then call the AJAX requester
624                 var page = previousPage[currentTabId];
625         }
626
627         // Request AJAX content
628         requestAjaxContent(prefix, htmlId, page);
629
630         // Change the footer navigation
631         enableFooterNavigation(prefix, page);
632 }
633
634 // Allows to save made changes (this will be called if the onchange event has been triggered)
635 function allowSaveChanges (element) {
636         // Mark element as changed, unmark as failed
637         $('#' + element).addClass('field_changed');
638         $('#' + element).removeClass('field_failed');
639
640         // Is the element not there?
641         if (!in_array(element, changedElements)) {
642                 // Mark the form as changed
643                 formChanged = true;
644
645                 // Make the 'Save changes' button clickable
646                 enableElement('#' + saveChangesId);
647
648                 // Remember this for later AJAX call
649                 changedElements.push(element);
650         } // END - if
651 }
652
653 // Marks all elements as unchanged/not failed
654 function markAllElementsAsUnchanged () {
655         // Remove status from all fields
656         for (var i = 0; i < changedElements.length; i++) {
657                 // Mark the element as changed
658                 $('#' + changedElements[i]).removeClass('field_changed');
659                 $('#' + changedElements[i]).removeClass('field_failed');
660         } // END - for
661 }
662
663 // Function to reset the form
664 function resetMailerAjaxForm () {
665         // Debug message
666         //* DEBUG: */ alert('resetMailerAjaxForm(): changedElements()=' + changedElements.length + ',saveChangesId=' + saveChangesId + ' - ENTERED!');
667
668         // Mark all elements as unchanged
669         markAllElementsAsUnchanged();
670
671         // Clear all changed elements
672         disableElement('input#' + saveChangesId);
673
674         // Reset changed elements and mark form as not changed
675         changedElements = new Array();
676         formChanged     = false;
677 }
678
679 // Mark given fields as failed
680 function markFormFieldsFailed (failedFields) {
681         // Mark all elements as failed
682         $.each(failedFields, function (i, field) {
683                 // Mark the element as 'failed'
684                 $('#' + field).removeClass('field_changed');
685                 $('#' + field).addClass('field_failed');
686
687                 // Also register it as 'changed', if not found
688                 if (!in_array(field, changedElements)) {
689                         // Okay, not yet added, so push it on the array
690                         changedElements.push(field);
691                 } // END - if
692         });
693 }
694
695 // Processes the content from AJAX call
696 function processAjaxResponseContent (prefix, ajax_content) {
697         // By default all is failed
698         var isResponseDone = false;
699
700         // Is 'status' and 'message' set?
701         if ((ajax_content.status == undefined) || (ajax_content.message == undefined)) {
702                 // No status/message set
703                 lastErrorMessage = 'Returned content does not provide &#36;status&#36; or &#36;message&#36; which is required.';
704         } else if ((ajax_content.status != 'done') && (ajax_content.status != 'failed')) {
705                 // This is also not good, only 'failed' or 'done' is supported
706                 lastErrorMessage = ajax_content.message + '<br />\nAdditionally an unknown status ' + ajax_content.status + ' was detected.';
707         } else if (ajax_content.status == 'failed') {
708                 // Something bad went wrong so copy the message
709                 lastErrorMessage = ajax_content.message;
710         } else {
711                 // All fine
712                 isResponseDone = true;
713         }
714
715         // Return status
716         return isResponseDone;
717 }
718
719 // Saves changes by sending the data to the AJAX backend script
720 function saveChanges (prefix) {
721         // Is process working?
722         if (isElementVisible(prefix, 'process')) {
723                 // Then exit silently
724                 return;
725         } // END - if
726
727         // Mark all elements as unchanged
728         markAllElementsAsUnchanged();
729
730         // Is there changed elements
731         if (changedElements.length == 0) {
732                 // This should not happen
733                 displayErrorWindow(prefix, '<div class="ajax_error_message">saveChanges() called with no changed elements.</div>');
734         } else if (saveChangesId == null) {
735                 // saveChangesId is not det
736                 displayErrorWindow(prefix, '<div class="ajax_error_message">saveChangesId is not set. Please add <em>saveChanges = \'foo_bar\';</em> to your code.</div>');
737         }
738
739         // Serialize the whole form
740         var serializedData = $('form').serialize();
741
742         // Hold the ready status
743         $.holdReady(true);
744         saveChangesPending = true;
745
746         /*
747          * Send the request to the AJAX backend, it doesn't matter from which page
748          * this was requested.
749          */
750         if (sendAjaxRequest(prefix, 'save_changes', '&tab=' + currentTabId + '&' + serializedData, true) == true) {
751                 // Get the content
752                 var ajax_content = getAjaxContent();
753
754                 // Process the returned content
755                 if (processAjaxResponseContent(prefix, ajax_content) == true) {
756                         // Mark all elements as unchanged
757                         markAllElementsAsUnchanged();
758
759                         // Reset form
760                         resetMailerAjaxForm();
761                 } else {
762                         // Is there 'failed_fields' set?
763                         if ((ajax_content.failed_fields != undefined) && (ajax_content.message != undefined)) {
764                                 // Mark all fields as 'failed'
765                                 markFormFieldsFailed(ajax_content.failed_fields);
766
767                                 // Display the error message
768                                 displayErrorWindow(prefix, '<div class="ajax_error_message">' + ajax_content.message + '</div>');
769                         } else {
770                                 // This didn't work, why?
771                                 displayErrorWindow(prefix, '<div class="ajax_error_message">processAjaxResponseContent() failed, please fix this.<br />\n' + lastErrorMessage + '</div>');
772                         }
773                 }
774
775                 // Saving changes may have worked
776                 saveChangesPending = false;
777         } else {
778                 // Mark all elements as unchanged
779                 markAllElementsAsUnchanged();
780
781                 // Display error message
782                 displayErrorWindow(prefix, getAjaxContent());
783
784                 // Saving changes didn't work
785                 saveChangesPending = false;
786         }
787
788         // Wait for all has finished
789         saveChangesLocked();
790 }
791
792 // Waiting for resources being loaded
793 function saveChangesLocked () {
794         // Has all been loaded?
795         if (saveChangesPending == false) {
796                 // Then release ready()
797                 $.holdReady(false);
798         } else {
799                 // Recursive call again
800                 window.setTimeout('saveChangesLocked()', 10);
801         }
802 }
803
804 // Saves changed settings and continues with given page (next/previous)
805 function doSaveChangesPage (prefix, htmlId, page) {
806         // Save the changes
807         saveChanges(prefix);
808
809         // Close the window
810         //* DEBUG: */ alert('doSaveChangesPage(): prefix=' + prefix + ',htmlId=' + htmlId + ',page=' + page + ' - calling closeWarningWindow()');
811         closeWarningWindow(prefix, true, false);
812
813         // Load requested page
814         doFooterPage(prefix, htmlId, page);
815 }
816
817 // Saves changed settings and continues with given tab
818 function doSaveChangesContinue (prefix, htmlId, tab) {
819         // Is process working?
820         if (isElementVisible(prefix, 'process')) {
821                 // Then exit silently
822                 return;
823         } // END - if
824
825         // Save the changes
826         saveChanges(prefix);
827
828         // Close the window
829         //* DEBUG: */ alert('doSaveChangesPage(): prefix=' + prefix + ',htmlId=' + htmlId + ',tab=' + tab + ' - calling closeWarningWindow()');
830         closeWarningWindow(prefix, true, false);
831
832         // Load requested content
833         requestAjaxContent(prefix, htmlId, tab);
834 }