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 data = new Array();
35 var formChanged = false;
36 var saveChangesId = null;
37 var lastErrorMessage = null;
38 var saveChangesPending = false;
40 // Add all footer navigation elements
41 footerElements[0] = 'next';
42 footerElements[1] = 'previous';
44 // Setter for current tab id
45 function setCurrentTabId (tabId) {
47 //* DEBUG: */ alert('currentTabId=' + currentTabId + ',tabId=' + tabId);
51 // Checks whether a given element is visible by checking 'display: none'
52 function isElementVisible (prefix, element) {
54 var el = document.getElementById(prefix + '_' + element);
57 if ((el == null) || (el == undefined)) {
58 throw new '"' + prefix + '_' + element + '" does not exist.';
59 } else if ((el.style.display == undefined) || (el.style.display == '')) {
60 throw new '"' + prefix + '_' + element + '" has no style.display element.';
64 var isVisible = (el.style.display != 'none');
65 //* DEBUG: */ alert(prefix + '_' + element + ':' + el.style.display + '=' + isVisible);
71 // Marks a tab navigation entry
72 function markTabNavigation (prefix, tab) {
73 // Is progress working?
74 if (isElementVisible(prefix, 'progress')) {
80 var li = document.getElementsByTagName('li');
82 // Set current tab name
83 var currentTabName = 'tab_enabled';
86 var tabName = prefix + '_' + tab;
88 // Search for all menus
89 for (var i = 0; i < li.length; i++) {
91 //* DEBUG: */ document.write('className=' + li[i].className + ',prefix=' + prefix + ',tab=' + tab + '<br />');
93 // Check for valid tab
94 if (li[i].className.substr(0, currentTabName.length) == currentTabName) {
95 // Okay does match (don't change any others)
96 if (li[i].id == tabName) {
98 $('li#' + tabName).addClass('tab_active');
99 //li[i].className += ' tab_active';
102 $('li#' + li[i].id).removeClass('tab_active');
103 //li[i].className = currentTabName;
109 // Enables a given element
110 function enableElement (element) {
111 // Remove class and attribute
112 $(element).removeClass('disabled');
113 $(element).removeAttr('disabled');
116 // Disables a given element
117 function disableElement (element) {
118 // Add class and attribute
119 $(element).addClass('disabled');
120 $(element).attr('disabled', 'disabled');
126 // Enables a given footer navigation element
127 function enableFooterNavigationPage (element) {
129 if (element == 'finish') {
130 // Then without '_page' suffix
131 enableElement('input#finish');
133 // Regular element with '_page' suffix
134 enableElement('input#' + element + '_page');
136 // Disable 'finish' element
137 disableElement('input#finish');
141 // Resets footer navigation by adding CSS class 'disabled'
142 function resetFooterNavigation () {
143 // Remove the 'disabled' class and attribute
144 for (var i = 0; i < footerElements.length; i++) {
145 disableElement('input#' + footerElements[i] + '_page');
148 // Disable this element
149 disableElement('input#finish');
152 // Getter for AJAX content
153 function getAjaxContent () {
155 if (data['ajax_content'] == undefined) {
157 throw new 'ajax_content requested but not set.';
161 return data['ajax_content'];
164 // Setter for AJAX content
165 function setAjaxContent (ajax_content) {
166 data['ajax_content'] = ajax_content;
169 // "Setter" for AJAX content but does decode the content
170 function setAjaxDecodedContent (ajax_content) {
171 // Decode URL-encoded data ...
172 var decoded = decodeUrlEncoding(ajax_content);
175 setAjaxContent(decoded);
178 // Getter for AJAX success
179 function getAjaxSuccess () {
180 return data['ajax_success'];
183 // Setter for AJAX success
184 function setAjaxSuccess (success) {
185 data['ajax_success'] = success;
188 // Set AJAX reply and decode JSON if requested
189 function setAjaxReply (reply, isJson) {
190 // Is it JSON URL-encoded content?
191 //* DEBUG: */ alert('setAjaxReply(): reply=' + reply + ',isJson=' + isJson);
192 if ((isJson != undefined) && (isJson == true)) {
193 // Decode URL-encoding (for some reason it must be here ...)
194 var localReply = decodeUrlEncoding(reply);
196 // Then decode it, replace '%20' with space before because '%20' breakes JSON content
197 var obj = jQuery.parseJSON(localReply.replace('%20', ' '));
202 // Handle the content over to decode it
203 setAjaxDecodedContent(reply);
207 // Sends out an AJAX request
208 function sendAjaxRequest (level, doValue, extra, isJson) {
209 // By default all requests failed
210 setAjaxSuccess(false);
212 // Reset AJAX content
215 // Send out the raw request
219 data: 'level=' + level + '&do=' + doValue + extra,
225 success: function (ajax_content) {
226 // Is ajax_content set?
227 if (ajax_content.reply_content == undefined) {
228 // This shall not happen
229 throw new 'ajax_content.reply_content not returned from ajax.php, please fix your scripts. (1)';
230 } else if (ajax_content.reply_content == null) {
231 // This shall not happen, too
232 throw new 'ajax_content.reply_content=null from ajax.php, please fix your scripts. (2)';
236 setAjaxReply(ajax_content.reply_content, isJson);
238 // Mark it as success
239 setAjaxSuccess(true);
242 // Called in case of an error (e.g. HTTP response status not '200 OK')
243 error: function (ajax_content) {
244 // Is ajax_content set?
245 if (ajax_content.reply_content == undefined) {
246 // Is 'responseText' there?
247 if (ajax_content.responseText != undefined) {
249 var obj = jQuery.parseJSON(ajax_content.responseText.replace('%20', ' '));
251 // Is 'reply_content' set?
252 if (obj.reply_content == undefined) {
253 // This shall not happen
254 throw new 'obj.reply_content not returned from ajax.php, please fix your scripts. (3)';
258 setAjaxReply(obj.reply_content, false);
261 // This shall not happen
262 throw new 'ajax_content.reply_content not returned from ajax.php, please fix your scripts. (4)';
264 } else if (ajax_content.reply_content == null) {
265 // This shall not happen, too
266 throw new 'ajax_content.reply_content=null from ajax.php, please fix your scripts. (5)';
270 setAjaxReply(ajax_content.reply_content, isJson);
275 //* DEBUG: */ alert(getAjaxSuccess() + ':' + level + ',' + doValue + ',' + extra + ',' + isJson);
276 return getAjaxSuccess();
279 // Enables footer navigation buttons
280 function enableFooterNavigation (prefix, tabId) {
281 // Is progress working?
282 if (isElementVisible(prefix, 'progress')) {
283 // Then exit silently
287 // Reset both footer navigation first
288 resetFooterNavigation();
290 // Do the AJAX request (JSON as content is enabled)
291 if (sendAjaxRequest(prefix, 'footer_navigation', '&tab=' + tabId, true) == true) {
293 $.each(getAjaxContent(), function (i, value) {
294 // Enable current element
295 enableFooterNavigationPage(value);
298 // Display error window
299 displayErrorWindow(prefix, getAjaxContent());
303 // Requests an AJAX content
304 function requestAjaxContent (prefix, htmlId, tabId, footerNavigation) {
305 // Is progress working?
306 if (isElementVisible(prefix, 'progress')) {
307 // Then exit silently
311 // Check if this request is disabled
312 if ($('#' + prefix + '_' + tabId).hasClass('tab_disabled')) {
313 // Clicked on a disabled tabId so blur it
314 //* DEBUG: */ alert('requestAjaxContent(): prefix=' + prefix + ',htmlId=' + htmlId + ',tabId=' + tabId + ' - DISABLED!');
316 } else if (formChanged == true) {
317 // Has changed form , so output message to browser
318 //* DEBUG: */ alert('requestAjaxContent(): prefix=' + prefix + ',htmlId=' + htmlId + ',tabId=' + tabId + ' - FORM CHANGED!');
319 displayChangedWarningWindow(prefix, tabId);
325 // Only request if content is different
326 //* DEBUG: */ alert('requestAjaxContent(): prefix=' + prefix + ',htmlId=' + htmlId + ',tabId=' + tabId + ',currentTabId=' + currentTabId);
327 if (tabId != currentTabId) {
328 // Set tabId as current
329 //* DEBUG: */ alert('requestAjaxContent(): prefix=' + prefix + ',htmlId=' + htmlId + ',tabId=' + tabId + ' - Calling setCurrentTabId()');
330 setCurrentTabId(tabId);
332 // Fade the old content out
333 $('#' + htmlId).fadeOut('fast', function() {
335 if (sendAjaxRequest(prefix, 'request_content', '&tab=' + tabId, false) == true) {
336 // Add the HTML content
337 $('#' + htmlId).html(getAjaxContent());
339 // Fade the content in
340 $('#' + htmlId).fadeIn('fast', function() {
341 // This differs, so mark the menu and request content
342 markTabNavigation(prefix, tabId);
344 // Is the footer navigation enabled?
345 if (footerNavigation == true) {
346 // Change footer navigation as well
347 enableFooterNavigation(prefix, tabId);
351 // Display error window
352 displayErrorWindow(prefix, getAjaxContent());
357 //* DEBUG: */ alert('SAME!');
361 // Displays a test window
362 function displayTestWindow (prefix, element) {
363 // Register click-event for error window
364 $('#' + prefix + '_error_close').click(function () {
366 closeErrorWindow(prefix, true, false);
369 // Register click-event for warning window
370 $('#' + prefix + '_warning_close').click(function () {
372 //* DEBUG: */ alert('displayTestWindow(): prefix=' + prefix + ' - calling closeWarningWindow()');
373 closeWarningWindow(prefix, true, false);
376 // Request it from the AJAX backend
377 if (sendAjaxRequest(prefix, 'test', '', false) == true) {
378 // Transfer the returned content to the prefix_warning_content id
379 $('#' + prefix + '_warning_content').html(getAjaxContent());
381 // Fade the warning in
382 $('#' + prefix + '_warning').fadeIn('slow', function() {
384 enableElement(element);
387 // Display error message
388 displayErrorWindow(prefix, getAjaxContent());
392 // Displays a warning window above the form to warn about changed&unsafed fields
393 function displayChangedWarningWindow (prefix, button) {
394 // Fade out warning window, if open
395 //* DEBUG: */ alert('displayChangedWarningWindow(): prefix=' + prefix + ',button=' + button + ' - calling closeWarningWindow()');
396 closeWarningWindow(prefix, true, false);
398 // Fade error out for eye-candy, if open
399 closeErrorWindow(prefix, true, false);
401 // Fade it out for eye-candy
402 closeProgressWindow(prefix, true, false);
404 // Abort here if warningDisplayed is still true
405 if (isElementVisible(prefix, 'warning')) {
406 // Make sure this doesn't happen
410 // Request it from the AJAX backend
411 if (sendAjaxRequest(prefix, 'change_warning', '&button=' + button + '&elements=' + changedElements.join(':')) == true) {
412 // Transfer the returned content to the prefix_warning_content id
413 $('#' + prefix + '_warning_content').html(getAjaxContent());
415 // Fade the warning in
416 $('#' + prefix + '_warning').fadeIn('slow', function() {
417 // Do nothing for now
420 // Display error message
421 displayErrorWindow(prefix, getAjaxContent());
425 // Displays the error window for given prefix and content
426 function displayErrorWindow (prefix, ajax_content) {
427 // Fade out warning window, if open
428 //* DEBUG: */ alert('displayErrorWindow(): prefix=' + prefix + ',ajax_content=' + ajax_content.reply_content + ' - calling closeWarningWindow()');
429 closeWarningWindow(prefix, true, false);
431 // Fade it out for eye-candy
432 closeErrorWindow(prefix, true, false);
434 // Fade it out for eye-candy
435 closeProgressWindow(prefix, true, false);
437 // Abort here if errorDisplayed is still true
438 if (isElementVisible(prefix, 'error')) {
439 // Make sure this doesn't happen
443 // Copy the response text to the error variable
444 if (ajax_content.reply_content != undefined) {
445 $('#' + prefix + '_error_content').html(ajax_content.reply_content);
447 $('#' + prefix + '_error_content').html(ajax_content);
451 $('#' + prefix + '_error').fadeIn('slow', function() {
452 // Do nothing for now
456 // Displays the progress window for given prefix and content
457 function displayProgressWindow (prefix, ajax_content) {
458 // Fade out warning window, if open
459 //* DEBUG: */ alert('displayProgressWindow(): prefix=' + prefix + ' - calling closeWarningWindow()');
460 closeWarningWindow(prefix, true, false);
462 // Fade it out for eye-candy
463 closeErrorWindow(prefix, true, false);
465 // Fade it out for eye-candy
466 closeProgressWindow(prefix, true, false);
468 // Abort here if progressDisplayed is still true
469 if (isElementVisible(prefix, 'progress')) {
470 // Make sure this doesn't happen
474 // Copy the response text to the progress variable
475 if (ajax_content.reply_content != undefined) {
477 setProgressContent(prefix, ajax_content.reply_content);
479 setProgressContent(prefix, ajax_content);
482 // Fade the progress in
483 $('#' + prefix + '_progress').fadeIn('slow', function() {
484 // Do nothing for now
488 // Sets "progress content"
489 function setProgressContent (prefix, content) {
491 $('#' + prefix + '_progress_content').html(content);
494 // Waits until the window has been closed
495 function closeErrorLocked (prefix) {
496 // Has all been loaded?
497 if (!isElementVisible(prefix, 'error')) {
498 // Then release ready()
501 // Recursive call again
502 window.setTimeout('closeErrorLocked(' + prefix + ')', 10);
506 // Waits until the window has been closed
507 function closeProgressLocked (prefix) {
508 // Has all been loaded?
509 if (!isElementVisible(prefix, 'progress')) {
510 // Then release ready()
513 // Recursive call again
514 window.setTimeout('closeProgressLocked(' + prefix + ')', 10);
518 // Closes an error window
519 function closeErrorWindow (prefix, waitClose, resetCurrentTabId) {
520 // Is the error displayed?
521 if (isElementVisible(prefix, 'error')) {
522 // Shall we wait ("sync") until the animation has completed?
523 if (waitClose == true) {
524 // Hold the ready status
528 // Yes, then fade it out
529 $('#' + prefix + '_error').fadeOut('fast', function() {
530 // Set current tab id to default
531 if (resetCurrentTabId == true) {
532 setCurrentTabId(defaultTabId);
536 // Shall this animation be "synchronized"?
537 if (waitClose == true) {
538 // Wait for the window has been closed
539 closeErrorLocked(prefix);
544 // Closes an progress window
545 function closeProgressWindow (prefix, waitClose, resetCurrentTabId) {
546 // Is the progress displayed?
547 if (isElementVisible(prefix, 'progress')) {
548 // Shall we wait ("sync") until the animation has completed?
549 if (waitClose == true) {
550 // Hold the ready status
554 // Yes, then fade it out
555 $('#' + prefix + '_progress').fadeOut('fast', function() {
556 // Set current tab id to default
557 if (resetCurrentTabId == true) {
558 setCurrentTabId(defaultTabId);
562 // Shall this animation be "synchronized"?
563 if (waitClose == true) {
564 // Wait for the window has been closed
565 closeProgressLocked(prefix);
570 // Waits until the window has been closed
571 function closeWarningLocked (prefix) {
572 // Has all been loaded?
573 if (!isElementVisible(prefix, 'warning')) {
574 // Then release ready()
577 // Recursive call again
578 window.setTimeout('closeWarningLocked(' + prefix + ')', 10);
582 // Closes an warning window
583 function closeWarningWindow (prefix, waitClose, resetCurrentTabId) {
584 //* DEBUG: */ alert('prefix=' + prefix + ',waitClose=' + waitClose + ' - ENTERED!');
585 // Is the warning displayed?
586 if (isElementVisible(prefix, 'warning')) {
587 // Shall we wait ("sync") until the animation has completed?
588 //* DEBUG: */ alert('prefix=' + prefix + ',waitClose=' + waitClose + ',warningDisplayed=true');
589 if (waitClose == true) {
590 // Hold the ready status
594 // Yes, then fade it out
595 $('#' + prefix + '_warning').fadeOut('fast', function() {
596 // Set current tab id to default
597 if (resetCurrentTabId == true) {
598 setCurrentTabId(defaultTabId);
602 // Shall this animation be "synchronized"?
603 if (waitClose == true) {
604 // Wait for the window has been closed
605 //* DEBUG: */ alert('prefix=' + prefix + ',waitClose=' + waitClose + ' - LOCKED!');
606 closeWarningLocked(prefix);
611 // A footer navigation button has been clicked
612 function doFooterPage (prefix, htmlId, button) {
613 //* DEBUG: */ alert('doFooterPage(): prefix=' + prefix + ',htmlId=' + htmlId + ',button=' + button + ' - ENTERED!');
614 // Has something being changed?
615 if (formChanged == true) {
616 // Output message to browser
617 displayChangedWarningWindow(prefix, button);
623 // Is progress working?
624 if (isElementVisible(prefix, 'progress')) {
625 // Then exit silently
629 // Is there a 'next' entry?
630 //* DEBUG: */ alert('doFooterPage(): button=' + button + ',currentTabId=' + currentTabId + ',nextPage[currentTabId]=' + nextPage[currentTabId]);
631 if ((button == 'next') && (nextPage[currentTabId] != null)) {
632 // Then call the AJAX requester
633 var page = nextPage[currentTabId];
634 } else if ((button == 'previous') && (previousPage[currentTabId] != null)) {
635 // Then call the AJAX requester
636 var page = previousPage[currentTabId];
639 // Request AJAX content
640 requestAjaxContent(prefix, htmlId, page);
642 // Change the footer navigation
643 enableFooterNavigation(prefix, page);
646 // Allows to save made changes (this will be called if the onchange event has been triggered)
647 function allowSaveChanges (element) {
648 // Mark element as changed, unmark as failed
649 $('#' + element).addClass('field_changed');
650 $('#' + element).removeClass('field_failed');
652 // Is the element not there?
653 if (!in_array(element, changedElements)) {
654 // Mark the form as changed
657 // Make the 'Save changes' button clickable
658 enableElement('#' + saveChangesId);
660 // Remember this for later AJAX call
661 changedElements.push(element);
665 // Marks all elements as unchanged/not failed
666 function markAllElementsAsUnchanged () {
667 // Remove status from all fields
668 for (var i = 0; i < changedElements.length; i++) {
669 // Mark the element as changed
670 $('#' + changedElements[i]).removeClass('field_changed');
671 $('#' + changedElements[i]).removeClass('field_failed');
675 // Function to reset the form
676 function resetMailerAjaxForm () {
678 //* DEBUG: */ alert('resetMailerAjaxForm(): changedElements()=' + changedElements.length + ',saveChangesId=' + saveChangesId + ' - ENTERED!');
680 // Mark all elements as unchanged
681 markAllElementsAsUnchanged();
683 // Clear all changed elements
684 disableElement('input#' + saveChangesId);
686 // Reset changed elements and mark form as not changed
687 changedElements = new Array();
691 // Mark given fields as failed
692 function markFormFieldsFailed (failedFields) {
693 // Mark all elements as failed
694 $.each(failedFields, function (i, field) {
695 // Mark the element as 'failed'
696 $('#' + field).removeClass('field_changed');
697 $('#' + field).addClass('field_failed');
699 // Also register it as 'changed', if not found
700 if (!in_array(field, changedElements)) {
701 // Okay, not yet added, so push it on the array
702 changedElements.push(field);
707 // Progresses the content from AJAX call
708 function progressAjaxResponseContent (prefix, ajax_content) {
709 // By default all is failed
710 var isResponseDone = false;
712 // Is 'status' and 'message' set?
713 if ((ajax_content.status == undefined) || (ajax_content.message == undefined)) {
714 // No status/message set
715 lastErrorMessage = 'Returned content does not provide $status$ or $message$ which is required.';
716 } else if ((ajax_content.status != 'done') && (ajax_content.status != 'failed')) {
717 // This is also not good, only 'failed' or 'done' is supported
718 lastErrorMessage = ajax_content.message + '<br />\nAdditionally an unknown status ' + ajax_content.status + ' was detected.';
719 } else if (ajax_content.status == 'failed') {
720 // Something bad went wrong so copy the message
721 lastErrorMessage = ajax_content.message;
724 isResponseDone = true;
728 return isResponseDone;
731 // Saves changes by sending the data to the AJAX backend script
732 function saveChanges (prefix) {
733 // Is progress working?
734 if (isElementVisible(prefix, 'progress')) {
735 // Then exit silently
739 // Mark all elements as unchanged
740 markAllElementsAsUnchanged();
742 // Is there changed elements
743 if (changedElements.length == 0) {
744 // This should not happen
745 displayErrorWindow(prefix, '<div class="ajax_error_message">saveChanges() called with no changed elements.</div>');
746 } else if (saveChangesId == null) {
747 // saveChangesId is not det
748 displayErrorWindow(prefix, '<div class="ajax_error_message">saveChangesId is not set. Please add <em>saveChanges = \'foo_bar\';</em> to your code.</div>');
751 // Serialize the whole form
752 var serializedData = $('form').serialize();
754 // Hold the ready status
756 saveChangesPending = true;
759 * Send the request to the AJAX backend, it doesn't matter from which page
760 * this was requested.
762 if (sendAjaxRequest(prefix, 'save_changes', '&tab=' + currentTabId + '&' + serializedData, true) == true) {
764 var ajax_content = getAjaxContent();
766 // Progress the returned content
767 if (progressAjaxResponseContent(prefix, ajax_content) == true) {
768 // Mark all elements as unchanged
769 markAllElementsAsUnchanged();
772 resetMailerAjaxForm();
774 // Is there 'failed_fields' set?
775 if ((ajax_content.failed_fields != undefined) && (ajax_content.message != undefined)) {
776 // Mark all fields as 'failed'
777 markFormFieldsFailed(ajax_content.failed_fields);
779 // Display the error message
780 displayErrorWindow(prefix, '<div class="ajax_error_message">' + ajax_content.message + '</div>');
782 // This didn't work, why?
783 displayErrorWindow(prefix, '<div class="ajax_error_message">progressAjaxResponseContent() failed, please fix this.<br />\n' + lastErrorMessage + '</div>');
787 // Saving changes may have worked
788 saveChangesPending = false;
790 // Mark all elements as unchanged
791 markAllElementsAsUnchanged();
793 // Display error message
794 displayErrorWindow(prefix, getAjaxContent());
796 // Saving changes didn't work
797 saveChangesPending = false;
800 // Wait for all has finished
804 // Waiting for resources being loaded
805 function saveChangesLocked () {
806 // Has all been loaded?
807 if (saveChangesPending == false) {
808 // Then release ready()
811 // Recursive call again
812 window.setTimeout('saveChangesLocked()', 10);
816 // Saves changed settings and continues with given page (next/previous)
817 function doSaveChangesPage (prefix, htmlId, page) {
822 //* DEBUG: */ alert('doSaveChangesPage(): prefix=' + prefix + ',htmlId=' + htmlId + ',page=' + page + ' - calling closeWarningWindow()');
823 closeWarningWindow(prefix, true, false);
825 // Load requested page
826 doFooterPage(prefix, htmlId, page);
829 // Saves changed settings and continues with given tab
830 function doSaveChangesContinue (prefix, htmlId, tab) {
831 // Is progress working?
832 if (isElementVisible(prefix, 'progress')) {
833 // Then exit silently
841 //* DEBUG: */ alert('doSaveChangesPage(): prefix=' + prefix + ',htmlId=' + htmlId + ',tab=' + tab + ' - calling closeWarningWindow()');
842 closeWarningWindow(prefix, true, false);
844 // Load requested content
845 requestAjaxContent(prefix, htmlId, tab);