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 defaultTabId = null;
32 var footerElements = new Array();
33 var changedElements = new Array();
34 var formChanged = false;
35 var saveChangesId = null;
36 var lastErrorMessage = null;
37 var saveChangesPending = false;
39 // Add all footer navigation elements
40 footerElements[0] = 'next';
41 footerElements[1] = 'previous';
43 // Setter for current tab id
44 function setCurrentTabId (tabId) {
46 //* DEBUG: */ alert('currentTabId=' + currentTabId + ',tabId=' + tabId);
50 // Checks whether a given element is visible by checking 'display: none'
51 function isElementVisible (prefix, element) {
53 var el = document.getElementById(prefix + '_' + element);
56 if (el == null || el == undefined) {
57 throw new '"' + prefix + '_' + element + '" does not exist.';
61 var isVisible = ((el.style.display == undefined) || ((el.style.display != 'none') && (el.style.display != '')));
67 // Marks a tab navigation entry
68 function markTabNavigation (prefix, tab) {
69 // Is process working?
70 if (isElementVisible(prefix, 'process')) {
76 var li = document.getElementsByTagName('li');
78 // Set current tab name
79 var currentTabName = 'tab_enabled';
82 var tabName = prefix + '_' + tab;
84 // Search for all menus
85 for (var i = 0; i < li.length; i++) {
87 //* DEBUG: */ document.write('className=' + li[i].className + ',prefix=' + prefix + ',tab=' + tab + '<br />');
89 // Check for valid tab
90 if (li[i].className.substr(0, currentTabName.length) == currentTabName) {
91 // Okay does match (don't change any others)
92 if (li[i].id == tabName) {
94 $('li#' + tabName).addClass('tab_active');
95 //li[i].className += ' tab_active';
98 $('li#' + li[i].id).removeClass('tab_active');
99 //li[i].className = currentTabName;
105 // Enables a given element
106 function enableElement (element) {
107 // Remove class and attribute
108 $(element).removeClass('disabled');
109 $(element).removeAttr('disabled');
112 // Disables a given element
113 function disableElement (element) {
114 // Add class and attribute
115 $(element).addClass('disabled');
116 $(element).attr('disabled', 'disabled');
122 // Enables a given footer navigation element
123 function enableFooterNavigationPage (element) {
124 // Remove the 'disabled' class and attribute
125 if (element == 'finish') {
126 enableElement('input#finish');
128 enableElement('input#' + element + '_page');
129 disableElement('input#finish');
133 // Resets footer navigation by adding CSS class 'disabled'
134 function resetFooterNavigation () {
135 // Remove the 'disabled' class and attribute
136 for (var i = 0; i < footerElements.length; i++) {
137 $('input#' + footerElements[i] + '_page').addClass('disabled');
138 $('input#' + footerElements[i] + '_page').attr('disabled', 'disabled');
139 $('input#' + footerElements[i] + '_page').blur();
143 // Getter for AJAX content
144 function getAjaxContent () {
146 if ($('body').data('ajax_content') == undefined) {
148 throw new 'ajax_content requested but not set.';
152 return $('body').data('ajax_content');
155 // "Setter" for AJAX content but does decode the content
156 function setAjaxDecodedContent (ajax_content) {
157 // Decode URL-encoded data ...
158 var decoded = decodeUrlEncoding(ajax_content);
161 setAjaxContent(decoded);
164 // Setter for AJAX content
165 function setAjaxContent (ajax_content) {
166 $('body').data('ajax_content', ajax_content);
169 // Getter for AJAX success
170 function getAjaxSuccess () {
171 return $('body').data('ajax_success');
174 // Setter for AJAX success
175 function setAjaxSuccess (success) {
176 $('body').data('ajax_success', success);
179 // Set AJAX reply and decode JSON if requested
180 function setAjaxReply (reply, isJson) {
181 // Is it JSON URL-encoded content?
182 if ((isJson != undefined) && (isJson == true)) {
183 // Decode URL-encoding (for some reason it must be here ...)
184 var localReply = decodeUrlEncoding(reply);
186 // Then decode it, replace '%20' with space before because '%20' breakes JSON content
187 var obj = jQuery.parseJSON(localReply.replace('%20', ' '));
192 // Handle the content over to decode it
193 setAjaxDecodedContent(reply);
197 // Sends out an AJAX request
198 function sendAjaxRequest (level, doValue, extra, isJson) {
199 // By default all requests failed
200 setAjaxSuccess(false);
202 // Reset AJAX content
205 // Send out the raw request
209 data: 'level=' + level + '&do=' + doValue + extra,
214 success: function (ajax_content) {
215 // Is ajax_content set?
216 if (ajax_content.reply_content == undefined) {
217 // This shall not happen
218 throw new 'ajax_content.reply_content not returned from ajax.php, please fix your scripts. (1)';
219 } else if (ajax_content.reply_content == null) {
220 // This shall not happen, too
221 throw new 'ajax_content.reply_content=null from ajax.php, please fix your scripts. (2)';
225 setAjaxReply(ajax_content.reply_content, isJson);
227 // Mark it as success
228 setAjaxSuccess(true);
231 // Called in case of an error (e.g. HTTP response status not '200 OK')
232 error: function (ajax_content) {
233 // Is ajax_content set?
234 if (ajax_content.reply_content == undefined) {
235 // Is 'responseText' there?
236 if (ajax_content.responseText != undefined) {
238 var obj = jQuery.parseJSON(ajax_content.responseText.replace('%20', ' '));
240 // Is 'reply_content' set?
241 if (obj.reply_content == undefined) {
242 // This shall not happen
243 throw new 'obj.reply_content not returned from ajax.php, please fix your scripts. (3)';
247 setAjaxReply(obj.reply_content, false);
250 // This shall not happen
251 throw new 'ajax_content.reply_content not returned from ajax.php, please fix your scripts. (4)';
253 } else if (ajax_content.reply_content == null) {
254 // This shall not happen, too
255 throw new 'ajax_content.reply_content=null from ajax.php, please fix your scripts. (5)';
259 setAjaxReply(ajax_content.reply_content, isJson);
264 return getAjaxSuccess();
267 // Enables footer navigation buttons
268 function enableFooterNavigation (prefix, tabId) {
269 // Is process working?
270 if (isElementVisible(prefix, 'process')) {
271 // Then exit silently
275 // Reset both footer navigation first
276 resetFooterNavigation();
278 // Do the AJAX request (JSON as content is enabled)
279 if (sendAjaxRequest(prefix, 'footer_navigation', '&tab=' + tabId, true) == true) {
281 $.each(getAjaxContent(), function (i, value) {
282 // Enable current element
283 enableFooterNavigationPage(value);
286 // Display error window
287 displayErrorWindow(prefix, getAjaxContent());
291 // Requests an AJAX content
292 function requestAjaxContent (prefix, htmlId, tabId, footerNavigation) {
293 // Is process working?
294 if (isElementVisible(prefix, 'process')) {
295 // Then exit silently
299 // Check if this request is disabled
300 if ($('#' + prefix + '_' + tabId).hasClass('tab_disabled')) {
301 // Clicked on a disabled tabId so blur it
302 //* DEBUG: */ alert('requestAjaxContent(): prefix=' + prefix + ',htmlId=' + htmlId + ',tabId=' + tabId + ' - DISABLED!');
304 } else if (formChanged == true) {
305 // Has changed form , so output message to browser
306 //* DEBUG: */ alert('requestAjaxContent(): prefix=' + prefix + ',htmlId=' + htmlId + ',tabId=' + tabId + ' - FORM CHANGED!');
307 displayChangedWarningWindow(prefix, tabId);
313 // Only request if content is different
314 //* DEBUG: */ alert('requestAjaxContent(): prefix=' + prefix + ',htmlId=' + htmlId + ',tabId=' + tabId + ',currentTabId=' + currentTabId);
315 if (tabId != currentTabId) {
316 // Set tabId as current
317 //* DEBUG: */ alert('requestAjaxContent(): prefix=' + prefix + ',htmlId=' + htmlId + ',tabId=' + tabId + ' - Calling setCurrentTabId()');
318 setCurrentTabId(tabId);
320 // Fade the old content out
321 $('#' + htmlId).fadeOut('fast', function() {
323 if (sendAjaxRequest(prefix, 'request_content', '&tab=' + tabId, false) == true) {
324 // Add the HTML content
325 $('#' + htmlId).html(getAjaxContent());
327 // Fade the content in
328 $('#' + htmlId).fadeIn('fast', function() {
329 // This differs, so mark the menu and request content
330 markTabNavigation(prefix, tabId);
332 // Is the footer navigation enabled?
333 if (footerNavigation == true) {
334 // Change footer navigation as well
335 enableFooterNavigation(prefix, tabId);
339 // Display error window
340 displayErrorWindow(prefix, getAjaxContent());
345 //* DEBUG: */ alert('SAME!');
349 // Displays a test window
350 function displayTestWindow (prefix, element) {
351 // Register click-event for error window
352 $('#' + prefix + '_error_close').click(function () {
354 closeErrorWindow(prefix);
357 // Register click-event for warning window
358 $('#' + prefix + '_warning_close').click(function () {
360 //* DEBUG: */ alert('displayTestWindow(): prefix=' + prefix + ' - calling closeWarningWindow()');
361 closeWarningWindow(prefix);
364 // Request it from the AJAX backend
365 if (sendAjaxRequest(prefix, 'test', '', false) == true) {
366 // Transfer the returned content to the prefix_warning_content id
367 $('#' + prefix + '_warning_content').html(getAjaxContent());
369 // Fade the warning in
370 $('#' + prefix + '_warning').fadeIn('slow', function() {
372 enableElement(element);
375 // Display error message
376 displayErrorWindow(prefix, getAjaxContent());
380 // Displays a warning window above the form to warn about changed&unsafed fields
381 function displayChangedWarningWindow (prefix, button) {
382 // Fade out warning window, if open
383 //* DEBUG: */ alert('displayChangedWarningWindow(): prefix=' + prefix + ',button=' + button + ' - calling closeWarningWindow()');
384 closeWarningWindow(prefix);
386 // Fade error out for eye-candy, if open
387 closeErrorWindow(prefix);
389 // Fade it out for eye-candy
390 closeProcessWindow(prefix);
392 // Abort here if warningDisplayed is still true
393 if (isElementVisible(prefix, 'warning')) {
394 // Make sure this doesn't happen
398 // Request it from the AJAX backend
399 if (sendAjaxRequest(prefix, 'change_warning', '&button=' + button + '&elements=' + changedElements.join(':')) == true) {
400 // Transfer the returned content to the prefix_warning_content id
401 $('#' + prefix + '_warning_content').html(getAjaxContent());
403 // Fade the warning in
404 $('#' + prefix + '_warning').fadeIn('slow', function() {
405 // Do nothing for now
408 // Display error message
409 displayErrorWindow(prefix, getAjaxContent());
413 // Displays the error window for given prefix and content
414 function displayErrorWindow (prefix, ajax_content) {
415 // Fade out warning window, if open
416 //* DEBUG: */ alert('displayErrorWindow(): prefix=' + prefix + ' - calling closeWarningWindow()');
417 closeWarningWindow(prefix);
419 // Fade it out for eye-candy
420 closeErrorWindow(prefix);
422 // Fade it out for eye-candy
423 closeProcessWindow(prefix);
425 // Abort here if errorDisplayed is still true
426 if (isElementVisible(prefix, 'error')) {
427 // Make sure this doesn't happen
431 // Copy the response text to the error variable
432 if (ajax_content.reply_content != undefined) {
433 $('#' + prefix + '_error_content').html(ajax_content.reply_content);
435 $('#' + prefix + '_error_content').html(ajax_content);
439 $('#' + prefix + '_error').fadeIn('slow', function() {
440 // Do nothing for now
444 // Displays the process window for given prefix and content
445 function displayProcessWindow (prefix, ajax_content) {
446 // Fade out warning window, if open
447 //* DEBUG: */ alert('displayProcessWindow(): prefix=' + prefix + ' - calling closeWarningWindow()');
448 closeWarningWindow(prefix);
450 // Fade it out for eye-candy
451 closeErrorWindow(prefix);
453 // Fade it out for eye-candy
454 closeProcessWindow(prefix);
456 // Abort here if processDisplayed is still true
457 if (isElementVisible(prefix, 'process')) {
458 // Make sure this doesn't happen
462 // Copy the response text to the process variable
463 if (ajax_content.reply_content != undefined) {
465 setProcessContent(prefix, ajax_content.reply_content);
467 setProcessContent(prefix, ajax_content);
470 // Fade the process in
471 $('#' + prefix + '_process').fadeIn('slow', function() {
472 // Do nothing for now
476 // Sets "process content"
477 function setProcessContent (prefix, content) {
479 $('#' + prefix + '_process_content').html(content);
482 // Waits until the window has been closed
483 function closeErrorLocked () {
484 // Has all been loaded?
485 if (!isElementVisible(prefix, 'error')) {
486 // Then release ready()
489 // Recursive call again
490 window.setTimeout('closeErrorLocked()', 10);
494 // Waits until the window has been closed
495 function closeProcessLocked () {
496 // Has all been loaded?
497 if (!isElementVisible(prefix, 'process')) {
498 // Then release ready()
501 // Recursive call again
502 window.setTimeout('closeProcessLocked()', 10);
506 // Closes an error window
507 function closeErrorWindow (prefix, waitClose, resetCurrentTabId) {
508 // Is the error displayed?
509 if (isElementVisible(prefix, 'error')) {
510 // Shall we wait ("sync") until the animation has completed?
511 if (waitClose == true) {
512 // Hold the ready status
516 // Yes, then fade it out
517 $('#' + prefix + '_error').fadeOut('fast', function() {
518 // Set current tab id to default
519 if (resetCurrentTabId == true) {
520 setCurrentTabId(defaultTabId);
524 // Shall this animation be "synchronized"?
525 if (waitClose == true) {
526 // Wait for the window has been closed
532 // Closes an process window
533 function closeProcessWindow (prefix, waitClose, resetCurrentTabId) {
534 // Is the process displayed?
535 if (isElementVisible(prefix, 'process')) {
536 // Shall we wait ("sync") until the animation has completed?
537 if (waitClose == true) {
538 // Hold the ready status
542 // Yes, then fade it out
543 $('#' + prefix + '_process').fadeOut('fast', function() {
544 // Set current tab id to default
545 if (resetCurrentTabId == true) {
546 setCurrentTabId(defaultTabId);
550 // Shall this animation be "synchronized"?
551 if (waitClose == true) {
552 // Wait for the window has been closed
553 closeProcessLocked();
558 // Waits until the window has been closed
559 function closeWarningLocked () {
560 // Has all been loaded?
561 if (!isElementVisible(prefix, 'warning')) {
562 // Then release ready()
565 // Recursive call again
566 window.setTimeout('closeWarningLocked()', 10);
570 // Closes an warning window
571 function closeWarningWindow (prefix, waitClose, resetCurrentTabId) {
572 //* DEBUG: */ alert('prefix=' + prefix + ',waitClose=' + waitClose + ' - ENTERED!');
573 // Is the warning displayed?
574 if (isElementVisible(prefix, 'warning')) {
575 // Shall we wait ("sync") until the animation has completed?
576 //* DEBUG: */ alert('prefix=' + prefix + ',waitClose=' + waitClose + ',warningDisplayed=true');
577 if (waitClose == true) {
578 // Hold the ready status
582 // Yes, then fade it out
583 $('#' + prefix + '_warning').fadeOut('fast', function() {
584 // Set current tab id to default
585 if (resetCurrentTabId == true) {
586 setCurrentTabId(defaultTabId);
590 // Shall this animation be "synchronized"?
591 if (waitClose == true) {
592 // Wait for the window has been closed
593 //* DEBUG: */ alert('prefix=' + prefix + ',waitClose=' + waitClose + ' - LOCKED!');
594 closeWarningLocked();
599 // A footer navigation button has been clicked
600 function doFooterPage (prefix, htmlId, button) {
601 //* DEBUG: */ alert('doFooterPage(): prefix=' + prefix + ',htmlId=' + htmlId + ',button=' + button + ' - ENTERED!');
602 // Has something being changed?
603 if (formChanged == true) {
604 // Output message to browser
605 displayChangedWarningWindow(prefix, button);
611 // Is process working?
612 if (isElementVisible(prefix, 'process')) {
613 // Then exit silently
617 // Is there a 'next' entry?
618 //* DEBUG: */ alert('doFooterPage(): button=' + button + ',currentTabId=' + currentTabId + ',nextPage[currentTabId]=' + nextPage[currentTabId]);
619 if ((button == 'next') && (nextPage[currentTabId] != null)) {
620 // Then call the AJAX requester
621 var page = nextPage[currentTabId];
622 } else if ((button == 'previous') && (previousPage[currentTabId] != null)) {
623 // Then call the AJAX requester
624 var page = previousPage[currentTabId];
627 // Request AJAX content
628 requestAjaxContent(prefix, htmlId, page);
630 // Change the footer navigation
631 enableFooterNavigation(prefix, page);
634 // Allows to save made changes (this will be called if the onchange event has been triggered)
635 function allowSaveChanges (element) {
636 // Mark element as changed, unmark as failed
637 $('#' + element).addClass('field_changed');
638 $('#' + element).removeClass('field_failed');
640 // Is the element not there?
641 if (!in_array(element, changedElements)) {
642 // Mark the form as changed
645 // Make the 'Save changes' button clickable
646 enableElement('#' + saveChangesId);
648 // Remember this for later AJAX call
649 changedElements.push(element);
653 // Marks all elements as unchanged/not failed
654 function markAllElementsAsUnchanged () {
655 // Remove status from all fields
656 for (var i = 0; i < changedElements.length; i++) {
657 // Mark the element as changed
658 $('#' + changedElements[i]).removeClass('field_changed');
659 $('#' + changedElements[i]).removeClass('field_failed');
663 // Function to reset the form
664 function resetMailerAjaxForm () {
666 //* DEBUG: */ alert('resetMailerAjaxForm(): changedElements()=' + changedElements.length + ',saveChangesId=' + saveChangesId + ' - ENTERED!');
668 // Mark all elements as unchanged
669 markAllElementsAsUnchanged();
671 // Clear all changed elements
672 disableElement('input#' + saveChangesId);
674 // Reset changed elements and mark form as not changed
675 changedElements = new Array();
679 // Mark given fields as failed
680 function markFormFieldsFailed (failedFields) {
681 // Mark all elements as failed
682 $.each(failedFields, function (i, field) {
683 // Mark the element as 'failed'
684 $('#' + field).removeClass('field_changed');
685 $('#' + field).addClass('field_failed');
687 // Also register it as 'changed', if not found
688 if (!in_array(field, changedElements)) {
689 // Okay, not yet added, so push it on the array
690 changedElements.push(field);
695 // Processes the content from AJAX call
696 function processAjaxResponseContent (prefix, ajax_content) {
697 // By default all is failed
698 var isResponseDone = false;
700 // Is 'status' and 'message' set?
701 if ((ajax_content.status == undefined) || (ajax_content.message == undefined)) {
702 // No status/message set
703 lastErrorMessage = 'Returned content does not provide $status$ or $message$ which is required.';
704 } else if ((ajax_content.status != 'done') && (ajax_content.status != 'failed')) {
705 // This is also not good, only 'failed' or 'done' is supported
706 lastErrorMessage = ajax_content.message + '<br />\nAdditionally an unknown status ' + ajax_content.status + ' was detected.';
707 } else if (ajax_content.status == 'failed') {
708 // Something bad went wrong so copy the message
709 lastErrorMessage = ajax_content.message;
712 isResponseDone = true;
716 return isResponseDone;
719 // Saves changes by sending the data to the AJAX backend script
720 function saveChanges (prefix) {
721 // Is process working?
722 if (isElementVisible(prefix, 'process')) {
723 // Then exit silently
727 // Mark all elements as unchanged
728 markAllElementsAsUnchanged();
730 // Is there changed elements
731 if (changedElements.length == 0) {
732 // This should not happen
733 displayErrorWindow(prefix, '<div class="ajax_error_message">saveChanges() called with no changed elements.</div>');
734 } else if (saveChangesId == null) {
735 // saveChangesId is not det
736 displayErrorWindow(prefix, '<div class="ajax_error_message">saveChangesId is not set. Please add <em>saveChanges = \'foo_bar\';</em> to your code.</div>');
739 // Serialize the whole form
740 var serializedData = $('form').serialize();
742 // Hold the ready status
744 saveChangesPending = true;
747 * Send the request to the AJAX backend, it doesn't matter from which page
748 * this was requested.
750 if (sendAjaxRequest(prefix, 'save_changes', '&tab=' + currentTabId + '&' + serializedData, true) == true) {
752 var ajax_content = getAjaxContent();
754 // Process the returned content
755 if (processAjaxResponseContent(prefix, ajax_content) == true) {
756 // Mark all elements as unchanged
757 markAllElementsAsUnchanged();
760 resetMailerAjaxForm();
762 // Is there 'failed_fields' set?
763 if ((ajax_content.failed_fields != undefined) && (ajax_content.message != undefined)) {
764 // Mark all fields as 'failed'
765 markFormFieldsFailed(ajax_content.failed_fields);
767 // Display the error message
768 displayErrorWindow(prefix, '<div class="ajax_error_message">' + ajax_content.message + '</div>');
770 // This didn't work, why?
771 displayErrorWindow(prefix, '<div class="ajax_error_message">processAjaxResponseContent() failed, please fix this.<br />\n' + lastErrorMessage + '</div>');
775 // Saving changes may have worked
776 saveChangesPending = false;
778 // Mark all elements as unchanged
779 markAllElementsAsUnchanged();
781 // Display error message
782 displayErrorWindow(prefix, getAjaxContent());
784 // Saving changes didn't work
785 saveChangesPending = false;
788 // Wait for all has finished
792 // Waiting for resources being loaded
793 function saveChangesLocked () {
794 // Has all been loaded?
795 if (saveChangesPending == false) {
796 // Then release ready()
799 // Recursive call again
800 window.setTimeout('saveChangesLocked()', 10);
804 // Saves changed settings and continues with given page (next/previous)
805 function doSaveChangesPage (prefix, htmlId, page) {
810 //* DEBUG: */ alert('doSaveChangesPage(): prefix=' + prefix + ',htmlId=' + htmlId + ',page=' + page + ' - calling closeWarningWindow()');
811 closeWarningWindow(prefix, true, false);
813 // Load requested page
814 doFooterPage(prefix, htmlId, page);
817 // Saves changed settings and continues with given tab
818 function doSaveChangesContinue (prefix, htmlId, tab) {
819 // Is process working?
820 if (isElementVisible(prefix, 'process')) {
821 // Then exit silently
829 //* DEBUG: */ alert('doSaveChangesPage(): prefix=' + prefix + ',htmlId=' + htmlId + ',tab=' + tab + ' - calling closeWarningWindow()');
830 closeWarningWindow(prefix, true, false);
832 // Load requested content
833 requestAjaxContent(prefix, htmlId, tab);