2 * Common JavaScript functions for AJAX requests (they are general purpose).
3 * --------------------------------------------------------------------
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
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.
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.
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,
30 var currentTabId = null;
31 var processDisplayed = false;
32 var errorDisplayed = false;
33 var warningDisplayed = false;
34 var defaultTabId = null;
35 var footerElements = new Array();
36 var changedElements = new Array();
37 var formChanged = false;
38 var saveChangesId = null;
39 var lastErrorMessage = null;
40 var saveChangesPending = false;
42 // Add all footer navigation elements
43 footerElements[0] = 'next';
44 footerElements[1] = 'previous';
46 // Setter for current tab id
47 function setCurrentTabId (tabId) {
49 //* DEBUG: */ alert('currentTabId=' + currentTabId + ',tabId=' + tabId);
53 // Marks a tab navigation entry
54 function markTabNavigation (prefix, tab) {
55 // Is process working?
56 if (processDisplayed == true) {
62 var li = document.getElementsByTagName('li');
64 // Set current tab name
65 var currentTabName = 'tab_enabled';
68 var tabName = prefix + '_' + tab;
70 // Search for all menus
71 for (var i = 0; i < li.length; i++) {
73 //* DEBUG: */ document.write('className=' + li[i].className + ',prefix=' + prefix + ',tab=' + tab + '<br />');
75 // Check for valid tab
76 if (li[i].className.substr(0, currentTabName.length) == currentTabName) {
77 // Okay does match (don't change any others)
78 if (li[i].id == tabName) {
80 $('li#' + tabName).addClass('tab_active');
81 //li[i].className += ' tab_active';
84 $('li#' + li[i].id).removeClass('tab_active');
85 //li[i].className = currentTabName;
91 // Enables a given element
92 function enableElement (element) {
93 // Remove class and attribute
94 $(element).removeClass('disabled');
95 $(element).removeAttr('disabled');
98 // Disables a given element
99 function disableElement (element) {
100 // Add class and attribute
101 $(element).addClass('disabled');
102 $(element).attr('disabled', 'disabled');
108 // Enables a given footer navigation element
109 function enableFooterNavigationPage (element) {
110 // Remove the 'disabled' class and attribute
111 if (element == 'finish') {
112 enableElement('input#finish');
114 enableElement('input#' + element + '_page');
115 disableElement('input#finish');
119 // Resets footer navigation by adding CSS class 'disabled'
120 function resetFooterNavigation () {
121 // Remove the 'disabled' class and attribute
122 for (var i = 0; i < footerElements.length; i++) {
123 $('input#' + footerElements[i] + '_page').addClass('disabled');
124 $('input#' + footerElements[i] + '_page').attr('disabled', 'disabled');
125 $('input#' + footerElements[i] + '_page').blur();
129 // Getter for AJAX content
130 function getAjaxContent () {
132 if ($('body').data('ajax_content') == undefined) {
134 throw new 'ajax_content requested but not set.';
138 return $('body').data('ajax_content');
141 // "Setter" for AJAX content but does decode the content
142 function setAjaxDecodedContent (ajax_content) {
143 // Decode URL-encoded data ...
144 var decoded = decodeUrlEncoding(ajax_content);
147 setAjaxContent(decoded);
150 // Setter for AJAX content
151 function setAjaxContent (ajax_content) {
152 $('body').data('ajax_content', ajax_content);
155 // Getter for AJAX success
156 function getAjaxSuccess () {
157 return $('body').data('ajax_success');
160 // Setter for AJAX success
161 function setAjaxSuccess (success) {
162 $('body').data('ajax_success', success);
165 // Set AJAX reply and decode JSON if requested
166 function setAjaxReply (reply, isJson) {
167 // Is it JSON URL-encoded content?
168 if ((isJson != undefined) && (isJson == true)) {
169 // Decode URL-encoding (for some reason it must be here ...)
170 var localReply = decodeUrlEncoding(reply);
172 // Then decode it, replace '%20' with space before because '%20' breakes JSON content
173 var obj = jQuery.parseJSON(localReply.replace('%20', ' '));
178 // Handle the content over to decode it
179 setAjaxDecodedContent(reply);
183 // Sends out an AJAX request
184 function sendAjaxRequest (level, doValue, extra, isJson) {
185 // By default all requests failed
186 setAjaxSuccess(false);
188 // Reset AJAX content
191 // Send out the raw request
195 data: 'level=' + level + '&do=' + doValue + extra,
200 success: function (ajax_content) {
201 // Is ajax_content set?
202 if (ajax_content.reply_content == undefined) {
203 // This shall not happen
204 throw new 'ajax_content.reply_content not returned from ajax.php, please fix your scripts. (1)';
205 } else if (ajax_content.reply_content == null) {
206 // This shall not happen, too
207 throw new 'ajax_content.reply_content=null from ajax.php, please fix your scripts. (2)';
211 setAjaxReply(ajax_content.reply_content, isJson);
213 // Mark it as success
214 setAjaxSuccess(true);
217 // Called in case of an error (e.g. HTTP response status not '200 OK')
218 error: function (ajax_content) {
219 // Is ajax_content set?
220 if (ajax_content.reply_content == undefined) {
221 // Is 'responseText' there?
222 if (ajax_content.responseText != undefined) {
224 var obj = jQuery.parseJSON(ajax_content.responseText.replace('%20', ' '));
226 // Is 'reply_content' set?
227 if (obj.reply_content == undefined) {
228 // This shall not happen
229 throw new 'obj.reply_content not returned from ajax.php, please fix your scripts. (3)';
233 setAjaxReply(obj.reply_content, false);
236 // This shall not happen
237 throw new 'ajax_content.reply_content not returned from ajax.php, please fix your scripts. (4)';
239 } else if (ajax_content.reply_content == null) {
240 // This shall not happen, too
241 throw new 'ajax_content.reply_content=null from ajax.php, please fix your scripts. (5)';
245 setAjaxReply(ajax_content.reply_content, isJson);
250 return getAjaxSuccess();
253 // Enables footer navigation buttons
254 function enableFooterNavigation (prefix, tabId) {
255 // Is process working?
256 if (processDisplayed == true) {
257 // Then exit silently
261 // Reset both footer navigation first
262 resetFooterNavigation();
264 // Do the AJAX request (JSON as content is enabled)
265 if (sendAjaxRequest(prefix, 'footer_navigation', '&tab=' + tabId, true) == true) {
267 $.each(getAjaxContent(), function (i, value) {
268 // Enable current element
269 enableFooterNavigationPage(value);
272 // Display error window
273 displayErrorWindow(prefix, getAjaxContent());
277 // Requests an AJAX content
278 function requestAjaxContent (prefix, htmlId, tabId, footerNavigation) {
279 // Is process working?
280 if (processDisplayed == true) {
281 // Then exit silently
285 // Check if this request is disabled
286 if ($('#' + prefix + '_' + tabId).hasClass('tab_disabled')) {
287 // Clicked on a disabled tabId so blur it
288 //* DEBUG: */ alert('requestAjaxContent(): prefix=' + prefix + ',htmlId=' + htmlId + ',tabId=' + tabId + ' - DISABLED!');
290 } else if (formChanged == true) {
291 // Has changed form , so output message to browser
292 //* DEBUG: */ alert('requestAjaxContent(): prefix=' + prefix + ',htmlId=' + htmlId + ',tabId=' + tabId + ' - FORM CHANGED!');
293 displayChangedWarningWindow(prefix, tabId);
299 // Only request if content is different
300 //* DEBUG: */ alert('requestAjaxContent(): prefix=' + prefix + ',htmlId=' + htmlId + ',tabId=' + tabId + ',currentTabId=' + currentTabId);
301 if (tabId != currentTabId) {
302 // Set tabId as current
303 //* DEBUG: */ alert('requestAjaxContent(): prefix=' + prefix + ',htmlId=' + htmlId + ',tabId=' + tabId + ' - Calling setCurrentTabId()');
304 setCurrentTabId(tabId);
306 // Fade the old content out
307 $('#' + htmlId).fadeOut('fast', function() {
309 if (sendAjaxRequest(prefix, 'request_content', '&tab=' + tabId, false) == true) {
310 // Add the HTML content
311 $('#' + htmlId).html(getAjaxContent());
313 // Fade the content in
314 $('#' + htmlId).fadeIn('fast', function() {
315 // This differs, so mark the menu and request content
316 markTabNavigation(prefix, tabId);
318 // Is the footer navigation enabled?
319 if (footerNavigation == true) {
320 // Change footer navigation as well
321 enableFooterNavigation(prefix, tabId);
325 // Display error window
326 displayErrorWindow(prefix, getAjaxContent());
331 //* DEBUG: */ alert('SAME!');
335 // Displays a test window
336 function displayTestWindow (prefix, element) {
337 // Register click-event for error window
338 $('#' + prefix + '_error_close').click(function () {
340 closeErrorWindow(prefix);
343 // Register click-event for warning window
344 $('#' + prefix + '_warning_close').click(function () {
346 //* DEBUG: */ alert('displayTestWindow(): prefix=' + prefix + ' - calling closeWarningWindow()');
347 closeWarningWindow(prefix);
350 // Request it from the AJAX backend
351 if (sendAjaxRequest(prefix, 'test', '', false) == true) {
352 // Transfer the returned content to the prefix_warning_content id
353 $('#' + prefix + '_warning_content').html(getAjaxContent());
355 // Fade the warning in
356 $('#' + prefix + '_warning').fadeIn('slow', function() {
358 enableElement(element);
361 // Mark 'warning' as displayed
362 warningDisplayed = true;
364 // Display error message
365 displayErrorWindow(prefix, getAjaxContent());
369 // Displays a warning window above the form to warn about changed&unsafed fields
370 function displayChangedWarningWindow (prefix, button) {
371 // Fade out warning window, if open
372 //* DEBUG: */ alert('displayChangedWarningWindow(): prefix=' + prefix + ',button=' + button + ' - calling closeWarningWindow()');
373 closeWarningWindow(prefix);
375 // Fade error out for eye-candy, if open
376 closeErrorWindow(prefix);
378 // Abort here if warningDisplayed is still true
379 if (warningDisplayed == true) {
380 // Make sure this doesn't happen
384 // Request it from the AJAX backend
385 if (sendAjaxRequest(prefix, 'change_warning', '&button=' + button + '&elements=' + changedElements.join(':')) == true) {
386 // Transfer the returned content to the prefix_warning_content id
387 $('#' + prefix + '_warning_content').html(getAjaxContent());
389 // Fade the warning in
390 $('#' + prefix + '_warning').fadeIn('slow', function() {
391 // Mark warning as displayed
392 warningDisplayed = true;
395 // Display error message
396 displayErrorWindow(prefix, getAjaxContent());
400 // Displays the error window for given prefix and content
401 function displayErrorWindow (prefix, ajax_content) {
402 // Fade out warning window, if open
403 //* DEBUG: */ alert('displayErrorWindow(): prefix=' + prefix + ' - calling closeWarningWindow()');
404 closeWarningWindow(prefix);
406 // Fade it out for eye-candy
407 closeErrorWindow(prefix);
409 // Abort here if errorDisplayed is still true
410 if (errorDisplayed == true) {
411 // Make sure this doesn't happen
415 // Copy the response text to the error variable
416 if (ajax_content.reply_content != undefined) {
417 $('#' + prefix + '_error_content').html(ajax_content.reply_content);
419 $('#' + prefix + '_error_content').html(ajax_content);
423 $('#' + prefix + '_error').fadeIn('slow', function() {
424 // Mark error as displayed
425 errorDisplayed = true;
429 // Displays the process window for given prefix and content
430 function displayProcessWindow (prefix, ajax_content) {
431 // Fade out warning window, if open
432 //* DEBUG: */ alert('displayProcessWindow(): prefix=' + prefix + ' - calling closeWarningWindow()');
433 closeWarningWindow(prefix);
435 // Fade it out for eye-candy
436 closeErrorWindow(prefix);
438 // Fade it out for eye-candy
439 closeProcessWindow(prefix);
441 // Abort here if processDisplayed is still true
442 if (processDisplayed == true) {
443 // Make sure this doesn't happen
447 // Copy the response text to the process variable
448 if (ajax_content.reply_content != undefined) {
449 $('#' + prefix + '_process_content').html(ajax_content.reply_content);
451 $('#' + prefix + '_process_content').html(ajax_content);
454 // Fade the process in
455 $('#' + prefix + '_process').fadeIn('slow', function() {
456 // Mark process as displayed
457 processDisplayed = true;
461 // Waits until the window has been closed
462 function closeErrorLocked () {
463 // Has all been loaded?
464 if (errorDisplayed == false) {
465 // Then release ready()
468 // Recursive call again
469 window.setTimeout('closeErrorLocked()', 10);
473 // Waits until the window has been closed
474 function closeProcessLocked () {
475 // Has all been loaded?
476 if (processDisplayed == false) {
477 // Then release ready()
480 // Recursive call again
481 window.setTimeout('closeProcessLocked()', 10);
485 // Closes an error window
486 function closeErrorWindow (prefix, waitClose, resetCurrentTabId) {
487 // Is the error displayed?
488 if (errorDisplayed == true) {
489 // Shall we wait ("sync") until the animation has completed?
490 if (waitClose == true) {
491 // Hold the ready status
495 // Yes, then fade it out
496 $('#' + prefix + '_error').fadeOut('fast', function() {
497 // Set current tab id to default
498 if (resetCurrentTabId == true) {
499 setCurrentTabId(defaultTabId);
503 errorDisplayed = false;
506 // Shall this animation be "synchronized"?
507 if (waitClose == true) {
508 // Wait for the window has been closed
514 // Closes an process window
515 function closeProcessWindow (prefix, waitClose, resetCurrentTabId) {
516 // Is the process displayed?
517 if (processDisplayed == true) {
518 // Shall we wait ("sync") until the animation has completed?
519 if (waitClose == true) {
520 // Hold the ready status
524 // Yes, then fade it out
525 $('#' + prefix + '_process').fadeOut('fast', function() {
526 // Set current tab id to default
527 if (resetCurrentTabId == true) {
528 setCurrentTabId(defaultTabId);
532 processDisplayed = false;
535 // Shall this animation be "synchronized"?
536 if (waitClose == true) {
537 // Wait for the window has been closed
538 closeProcessLocked();
543 // Waits until the window has been closed
544 function closeWarningLocked () {
545 // Has all been loaded?
546 if (warningDisplayed == false) {
547 // Then release ready()
550 // Recursive call again
551 window.setTimeout('closeWarningLocked()', 10);
555 // Closes an warning window
556 function closeWarningWindow (prefix, waitClose, resetCurrentTabId) {
557 //* DEBUG: */ alert('prefix=' + prefix + ',waitClose=' + waitClose + ' - ENTERED!');
558 // Is the warning displayed?
559 if (warningDisplayed == true) {
560 // Shall we wait ("sync") until the animation has completed?
561 //* DEBUG: */ alert('prefix=' + prefix + ',waitClose=' + waitClose + ',warningDisplayed=true');
562 if (waitClose == true) {
563 // Hold the ready status
567 // Yes, then fade it out
568 $('#' + prefix + '_warning').fadeOut('fast', function() {
569 // Set current tab id to default
570 //* DEBUG: */ alert('closeWarningWindow(): prefix=' + prefix + ',waitClose=' + waitClose + ',defaultTab=' + defaultTabId + ' - Calling setCurrentTabId()');
571 if (resetCurrentTabId == true) {
572 setCurrentTabId(defaultTabId);
576 warningDisplayed = false;
577 //* DEBUG: */ alert('closeWarningWindow(): waitClose=' + waitClose + ',resetCurrentTabId=' + resetCurrentTabId + ',warningDisplayed=false');
580 // Shall this animation be "synchronized"?
581 if (waitClose == true) {
582 // Wait for the window has been closed
583 //* DEBUG: */ alert('prefix=' + prefix + ',waitClose=' + waitClose + ' - LOCKED!');
584 closeWarningLocked();
589 // A footer navigation button has been clicked
590 function doFooterPage (prefix, htmlId, button) {
591 //* DEBUG: */ alert('doFooterPage(): prefix=' + prefix + ',htmlId=' + htmlId + ',button=' + button + ' - ENTERED!');
592 // Has something being changed?
593 if (formChanged == true) {
594 // Output message to browser
595 displayChangedWarningWindow(prefix, button);
601 // Is process working?
602 if (processDisplayed == true) {
603 // Then exit silently
607 // Is there a 'next' entry?
608 //* DEBUG: */ alert('doFooterPage(): button=' + button + ',currentTabId=' + currentTabId + ',nextPage[currentTabId]=' + nextPage[currentTabId]);
609 if ((button == 'next') && (nextPage[currentTabId] != null)) {
610 // Then call the AJAX requester
611 var page = nextPage[currentTabId];
612 } else if ((button == 'previous') && (previousPage[currentTabId] != null)) {
613 // Then call the AJAX requester
614 var page = previousPage[currentTabId];
617 // Request AJAX content
618 requestAjaxContent(prefix, htmlId, page);
620 // Change the footer navigation
621 enableFooterNavigation(prefix, page);
624 // Allows to save made changes (this will be called if the onchange event has been triggered)
625 function allowSaveChanges (element) {
626 // Mark element as changed, unmark as failed
627 $('#' + element).addClass('field_changed');
628 $('#' + element).removeClass('field_failed');
630 // Is the element not there?
631 if (!in_array(element, changedElements)) {
632 // Mark the form as changed
635 // Make the 'Save changes' button clickable
636 enableElement('#' + saveChangesId);
638 // Remember this for later AJAX call
639 changedElements.push(element);
643 // Marks all elements as unchanged/not failed
644 function markAllElementsAsUnchanged () {
645 // Remove status from all fields
646 for (var i = 0; i < changedElements.length; i++) {
647 // Mark the element as changed
648 $('#' + changedElements[i]).removeClass('field_changed');
649 $('#' + changedElements[i]).removeClass('field_failed');
653 // Function to reset the form
654 function resetMailerAjaxForm () {
656 //* DEBUG: */ alert('resetMailerAjaxForm(): changedElements()=' + changedElements.length + ',saveChangesId=' + saveChangesId + ' - ENTERED!');
658 // Mark all elements as unchanged
659 markAllElementsAsUnchanged();
661 // Clear all changed elements
662 disableElement('input#' + saveChangesId);
664 // Reset changed elements and mark form as not changed
665 changedElements = new Array();
669 // Mark given fields as failed
670 function markFormFieldsFailed (failedFields) {
671 // Mark all elements as failed
672 $.each(failedFields, function (i, field) {
673 // Mark the element as 'failed'
674 $('#' + field).removeClass('field_changed');
675 $('#' + field).addClass('field_failed');
677 // Also register it as 'changed', if not found
678 if (!in_array(field, changedElements)) {
679 // Okay, not yet added, so push it on the array
680 changedElements.push(field);
685 // Processes the content from AJAX call
686 function processAjaxResponseContent (prefix, ajax_content) {
687 // By default all is failed
688 var isResponseDone = false;
690 // Is 'status' and 'message' set?
691 if ((ajax_content.status == undefined) || (ajax_content.message == undefined)) {
692 // No status/message set
693 lastErrorMessage = 'Returned content does not provide $status$ or $message$ which is required.';
694 } else if ((ajax_content.status != 'done') && (ajax_content.status != 'failed')) {
695 // This is also not good, only 'failed' or 'done' is supported
696 lastErrorMessage = ajax_content.message + '<br />\nAdditionally an unknown status ' + ajax_content.status + ' was detected.';
697 } else if (ajax_content.status == 'failed') {
698 // Something bad went wrong so copy the message
699 lastErrorMessage = ajax_content.message;
702 isResponseDone = true;
706 return isResponseDone;
709 // Saves changes by sending the data to the AJAX backend script
710 function saveChanges (prefix) {
711 // Is process working?
712 if (processDisplayed == true) {
713 // Then exit silently
717 // Mark all elements as unchanged
718 markAllElementsAsUnchanged();
720 // Is there changed elements
721 if (changedElements.length == 0) {
722 // This should not happen
723 displayErrorWindow(prefix, '<div class="ajax_error_message">saveChanges() called with no changed elements.</div>');
724 } else if (saveChangesId == null) {
725 // saveChangesId is not det
726 displayErrorWindow(prefix, '<div class="ajax_error_message">saveChangesId is not set. Please add <em>saveChanges = \'foo_bar\';</em> to your code.</div>');
729 // Serialize the whole form
730 var serializedData = $('form').serialize();
732 // Hold the ready status
734 saveChangesPending = true;
737 * Send the request to the AJAX backend, it doesn't matter from which page
738 * this was requested.
740 if (sendAjaxRequest(prefix, 'save_changes', '&tab=' + currentTabId + '&' + serializedData, true) == true) {
742 var ajax_content = getAjaxContent();
744 // Process the returned content
745 if (processAjaxResponseContent(prefix, ajax_content) == true) {
746 // Mark all elements as unchanged
747 markAllElementsAsUnchanged();
750 resetMailerAjaxForm();
752 // Is there 'failed_fields' set?
753 if ((ajax_content.failed_fields != undefined) && (ajax_content.message != undefined)) {
754 // Mark all fields as 'failed'
755 markFormFieldsFailed(ajax_content.failed_fields);
757 // Display the error message
758 displayErrorWindow(prefix, '<div class="ajax_error_message">' + ajax_content.message + '</div>');
760 // This didn't work, why?
761 displayErrorWindow(prefix, '<div class="ajax_error_message">processAjaxResponseContent() failed, please fix this.<br />\n' + lastErrorMessage + '</div>');
765 // Saving changes may have worked
766 saveChangesPending = false;
768 // Mark all elements as unchanged
769 markAllElementsAsUnchanged();
771 // Display error message
772 displayErrorWindow(prefix, getAjaxContent());
774 // Saving changes didn't work
775 saveChangesPending = false;
778 // Wait for all has finished
782 // Waiting for resources being loaded
783 function saveChangesLocked () {
784 // Has all been loaded?
785 if (saveChangesPending == false) {
786 // Then release ready()
789 // Recursive call again
790 window.setTimeout('saveChangesLocked()', 10);
794 // Saves changed settings and continues with given page (next/previous)
795 function doSaveChangesPage (prefix, htmlId, page) {
800 //* DEBUG: */ alert('doSaveChangesPage(): prefix=' + prefix + ',htmlId=' + htmlId + ',page=' + page + ' - calling closeWarningWindow()');
801 closeWarningWindow(prefix, true, false);
803 // Load requested page
804 doFooterPage(prefix, htmlId, page);
807 // Saves changed settings and continues with given tab
808 function doSaveChangesContinue (prefix, htmlId, tab) {
809 // Is process working?
810 if (processDisplayed == true) {
811 // Then exit silently
819 //* DEBUG: */ alert('doSaveChangesPage(): prefix=' + prefix + ',htmlId=' + htmlId + ',tab=' + tab + ' - calling closeWarningWindow()');
820 closeWarningWindow(prefix, true, false);
822 // Load requested content
823 requestAjaxContent(prefix, htmlId, tab);