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