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