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