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 errorDisplayed = false;
32 var warningDisplayed = false;
33 var defaultTabId = null;
34 var footerElements = new Array();
35 var changedElements = new Array();
36 var formChanged = false;
37 var saveChangesId = null;
38 var lastErrorMessage = null;
39 var saveChangesPending = false;
41 // Add all footer navigation elements
42 footerElements[0] = 'next';
43 footerElements[1] = 'previous';
45 // Setter for current tab id
46 function setCurrentTabId (tabId) {
48 //* DEBUG: */ alert('currentTabId=' + currentTabId + ',tabId=' + tabId);
52 // Marks a tab navigation entry
53 function markTabNavigation (prefix, tab) {
55 var li = document.getElementsByTagName('li');
57 // Set current tab name
58 var currentTabName = 'tab_enabled';
61 var tabName = prefix + '_' + tab;
63 // Search for all menus
64 for (var i = 0; i < li.length; i++) {
66 //* DEBUG: */ document.write('className=' + li[i].className + ',prefix=' + prefix + ',tab=' + tab + '<br />');
68 // Check for valid tab
69 if (li[i].className.substr(0, currentTabName.length) == currentTabName) {
70 // Okay does match (don't change any others)
71 if (li[i].id == tabName) {
73 $('li#' + tabName).addClass('tab_active');
74 //li[i].className += ' tab_active';
77 $('li#' + li[i].id).removeClass('tab_active');
78 //li[i].className = currentTabName;
84 // Enables a given element
85 function enableElement (element) {
86 // Remove class and attribute
87 $(element).removeClass('disabled');
88 $(element).removeAttr('disabled');
91 // Disables a given element
92 function disableElement (element) {
93 // Add class and attribute
94 $(element).addClass('disabled');
95 $(element).attr('disabled', 'disabled');
101 // Enables a given footer navigation element
102 function enableFooterNavigationPage (element) {
103 // Remove the 'disabled' class and attribute
104 enableElement('input#' + element + '_page');
107 // Resets footer navigation by adding CSS class 'disabled'
108 function resetFooterNavigation () {
109 // Remove the 'disabled' class and attribute
110 for (var i = 0; i < footerElements.length; i++) {
111 $('input#' + footerElements[i] + '_page').addClass('disabled');
112 $('input#' + footerElements[i] + '_page').attr('disabled', 'disabled');
113 $('input#' + footerElements[i] + '_page').blur();
117 // Getter for AJAX content
118 function getAjaxContent () {
120 if ($('body').data('ajax_content') == undefined) {
122 throw new 'ajax_content requested but not set.';
126 return $('body').data('ajax_content');
129 // "Setter" for AJAX content but does decode the content
130 function setAjaxDecodedContent (ajax_content) {
131 // Decode URL-encoded data ...
132 var decoded = decodeUrlEncoding(ajax_content);
135 setAjaxContent(decoded);
138 // Setter for AJAX content
139 function setAjaxContent (ajax_content) {
140 $('body').data('ajax_content', ajax_content);
143 // Getter for AJAX success
144 function getAjaxSuccess () {
145 return $('body').data('ajax_success');
148 // Setter for AJAX success
149 function setAjaxSuccess (success) {
150 $('body').data('ajax_success', success);
153 // Set AJAX reply and decode JSON if requested
154 function setAjaxReply (reply, isJson) {
155 // Copy reply to local variable
156 var localReply = reply;
158 // Is it JSON URL-encoded content?
159 if ((isJson != undefined) && (isJson == true)) {
160 // Then decode it, replace '%20' with space before because '%20' breakes JSON content
161 var obj = jQuery.parseJSON(reply.replace('%20', ' '));
163 // Is reply_content there
164 if (obj.reply_content == undefined) {
166 throw new 'obj.reply_content not returned from ajax.php, please fix your scripts.';
170 setAjaxDecodedContent(obj.reply_content);
172 // Handle the content over to decode it
173 setAjaxDecodedContent(localReply);
177 // Sends out an AJAX request
178 function sendAjaxRequest (level, doValue, extra, isJson) {
179 // By default all requests failed
180 setAjaxSuccess(false);
182 // Reset AJAX content
185 // Send out the raw request
189 data: 'level=' + level + '&do=' + doValue + extra,
194 success: function (ajax_content) {
195 // Is ajax_content set?
196 if (ajax_content.reply_content == undefined) {
197 // This shall not happen
198 throw new 'ajax_content.reply_content not returned from ajax.php, please fix your scripts.';
199 } else if (ajax_content.reply_content == null) {
200 // This shall not happen, too
201 throw new 'ajax_content.reply_content=null from ajax.php, please fix your scripts.';
205 setAjaxReply(ajax_content.reply_content, isJson);
207 // Mark it as success
208 setAjaxSuccess(true);
211 // Called in case of an error (e.g. HTTP response status not '200 OK')
212 error: function (ajax_content) {
213 // Is ajax_content set?
214 if (ajax_content.reply_content == undefined) {
215 // This shall not happen
216 throw new 'ajax_content.reply_content not returned from ajax.php, please fix your scripts.';
217 } else if (ajax_content.reply_content == null) {
218 // This shall not happen, too
219 throw new 'ajax_content.reply_content=null from ajax.php, please fix your scripts.';
223 setAjaxReply(ajax_content.reply_content, isJson);
228 return getAjaxSuccess();
231 // Enables footer navigation buttons
232 function enableFooterNavigation (prefix, tabId) {
233 // Reset both footer navigation first
234 resetFooterNavigation();
236 // Do the AJAX request (JSON as content is enabled)
237 if (sendAjaxRequest(prefix, 'footer_navigation', '&tab=' + tabId, true) == true) {
239 $.each(getAjaxContent(), function (i, value) {
240 // Enable current element
241 enableFooterNavigationPage(value);
244 // Display error window
245 displayErrorWindow(prefix, getAjaxContent());
249 // Requests an AJAX content
250 function requestAjaxContent (prefix, htmlId, tabId, footerNavigation) {
251 // Check if this request is disabled
252 if ($('#' + prefix + '_' + tabId).hasClass('tab_disabled')) {
253 // Clicked on a disabled tabId so blur it
254 //* DEBUG: */ alert('requestAjaxContent(): prefix=' + prefix + ',htmlId=' + htmlId + ',tabId=' + tabId + ' - DISABLED!');
256 } else if (formChanged == true) {
257 // Has changed form , so output message to browser
258 //* DEBUG: */ alert('requestAjaxContent(): prefix=' + prefix + ',htmlId=' + htmlId + ',tabId=' + tabId + ' - FORM CHANGED!');
259 displayChangedWarningWindow(prefix, tabId);
265 // Only request if content is different
266 //* DEBUG: */ alert('requestAjaxContent(): prefix=' + prefix + ',htmlId=' + htmlId + ',tabId=' + tabId + ',currentTabId=' + currentTabId);
267 if (tabId != currentTabId) {
268 // Set tabId as current
269 //* DEBUG: */ alert('requestAjaxContent(): prefix=' + prefix + ',htmlId=' + htmlId + ',tabId=' + tabId + ' - Calling setCurrentTabId()');
270 setCurrentTabId(tabId);
272 // Fade the old content out
273 $('#' + htmlId).fadeOut('fast', function() {
275 if (sendAjaxRequest(prefix, 'request_content', '&tab=' + tabId, true) == true) {
276 // Add the HTML content
277 $('#' + htmlId).html(getAjaxContent());
279 // Fade the content in
280 $('#' + htmlId).fadeIn('fast', function() {
281 // This differs, so mark the menu and request content
282 markTabNavigation(prefix, tabId);
284 // Is the footer navigation enabled?
285 if (footerNavigation == true) {
286 // Change footer navigation as well
287 enableFooterNavigation(prefix, tabId);
291 // Display error window
292 displayErrorWindow(prefix, getAjaxContent());
297 //* DEBUG: */ alert('SAME!');
301 // Displays a test window
302 function displayTestWindow (prefix, element) {
303 // Register click-event for error window
304 $('#' + prefix + '_error_close').click(function () {
306 closeErrorWindow(prefix);
309 // Register click-event for warning window
310 $('#' + prefix + '_warning_close').click(function () {
312 //* DEBUG: */ alert('displayTestWindow(): prefix=' + prefix + ' - calling closeWarningWindow()');
313 closeWarningWindow(prefix);
316 // Request it from the AJAX backend
317 if (sendAjaxRequest(prefix, 'test', '') == true) {
318 // Transfer the returned content to the prefix_warning_content id
319 $('#' + prefix + '_warning_content').html(getAjaxContent());
321 // Fade the warning in
322 $('#' + prefix + '_warning').fadeIn('slow', function() {
324 enableElement(element);
327 // Mark 'warning' as displayed
328 warningDisplayed = true;
330 // Display error message
331 displayErrorWindow(prefix, getAjaxContent());
335 // Displays a warning window above the form to warn about changed&unsafed fields
336 function displayChangedWarningWindow (prefix, button) {
337 // Fade out warning window, if open
338 //* DEBUG: */ alert('displayChangedWarningWindow(): prefix=' + prefix + ',button=' + button + ' - calling closeWarningWindow()');
339 closeWarningWindow(prefix);
341 // Fade error out for eye-candy, if open
342 closeErrorWindow(prefix);
344 // Abort here if warningDisplayed is still true
345 if (warningDisplayed == true) {
346 // Make sure this doesn't happen
350 // Request it from the AJAX backend
351 if (sendAjaxRequest(prefix, 'change_warning', '&button=' + button + '&elements=' + changedElements.join(':')) == 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() {
357 // Mark warning as displayed
358 warningDisplayed = true;
361 // Display error message
362 displayErrorWindow(prefix, getAjaxContent());
366 // Displays the error window for given prefix and content
367 function displayErrorWindow (prefix, ajax_content) {
368 // Fade out warning window, if open
369 //* DEBUG: */ alert('displayErrorWindow(): prefix=' + prefix + ' - calling closeWarningWindow()');
370 closeWarningWindow(prefix);
372 // Fade it out for eye-candy
373 closeErrorWindow(prefix);
375 // Abort here if errorDisplayed is still true
376 if (errorDisplayed == true) {
377 // Make sure this doesn't happen
381 // Copy the response text to the error variable
382 if (ajax_content.reply_content != undefined) {
383 $('#' + prefix + '_error_content').html(ajax_content.reply_content);
385 $('#' + prefix + '_error_content').html(ajax_content);
389 $('#' + prefix + '_error').fadeIn('slow', function() {
390 // Mark error as displayed
391 errorDisplayed = true;
395 // Waits until the window has been closed
396 function closeErrorLocked () {
397 // Has all been loaded?
398 if (errorDisplayed == false) {
399 // Then release ready()
402 // Recursive call again
403 window.setTimeout('closeErrorLocked()', 10);
407 // Closes an error window
408 function closeErrorWindow (prefix, waitClose, resetCurrentTabId) {
409 // Is the error displayed?
410 if (errorDisplayed == true) {
411 // Shall we wait ("sync") until the animation has completed?
412 if (waitClose == true) {
413 // Hold the ready status
417 // Yes, then fade it out
418 $('#' + prefix + '_error').fadeOut('fast', function() {
419 // Set current tab id to default
420 if (resetCurrentTabId == true) {
421 setCurrentTabId(defaultTabId);
425 errorDisplayed = false;
428 // Shall this animation be "synchronized"?
429 if (waitClose == true) {
430 // Wait for the window has been closed
436 // Waits until the window has been closed
437 function closeWarningLocked () {
438 // Has all been loaded?
439 if (warningDisplayed == false) {
440 // Then release ready()
443 // Recursive call again
444 window.setTimeout('closeWarningLocked()', 10);
448 // Closes an warning window
449 function closeWarningWindow (prefix, waitClose, resetCurrentTabId) {
450 //* DEBUG: */ alert('prefix=' + prefix + ',waitClose=' + waitClose + ' - ENTERED!');
451 // Is the warning displayed?
452 if (warningDisplayed == true) {
453 // Shall we wait ("sync") until the animation has completed?
454 //* DEBUG: */ alert('prefix=' + prefix + ',waitClose=' + waitClose + ',warningDisplayed=true');
455 if (waitClose == true) {
456 // Hold the ready status
460 // Yes, then fade it out
461 $('#' + prefix + '_warning').fadeOut('fast', function() {
462 // Set current tab id to default
463 //* DEBUG: */ alert('closeWarningWindow(): prefix=' + prefix + ',waitClose=' + waitClose + ',defaultTab=' + defaultTabId + ' - Calling setCurrentTabId()');
464 if (resetCurrentTabId == true) {
465 setCurrentTabId(defaultTabId);
469 warningDisplayed = false;
470 //* DEBUG: */ alert('closeWarningWindow(): waitClose=' + waitClose + ',resetCurrentTabId=' + resetCurrentTabId + ',warningDisplayed=false');
473 // Shall this animation be "synchronized"?
474 if (waitClose == true) {
475 // Wait for the window has been closed
476 //* DEBUG: */ alert('prefix=' + prefix + ',waitClose=' + waitClose + ' - LOCKED!');
477 closeWarningLocked();
482 // A footer navigation button has been clicked
483 function doFooterPage (prefix, htmlId, button) {
484 //* DEBUG: */ alert('doFooterPage(): prefix=' + prefix + ',htmlId=' + htmlId + ',button=' + button + ' - ENTERED!');
485 // Has something being changed?
486 if (formChanged == true) {
487 // Output message to browser
488 displayChangedWarningWindow(prefix, button);
494 // Do we have a 'next' entry?
495 //* DEBUG: */ alert('doFooterPage(): button=' + button + ',currentTabId=' + currentTabId + ',nextPage[currentTabId]=' + nextPage[currentTabId]);
496 if ((button == 'next') && (nextPage[currentTabId] != null)) {
497 // Then call the AJAX requester
498 requestAjaxContent(prefix, htmlId, nextPage[currentTabId]);
499 } else if ((button == 'previous') && (previousPage[currentTabId] != null)) {
500 // Then call the AJAX requester
501 requestAjaxContent(prefix, htmlId, previousPage[currentTabId]);
505 // Allows to save made changes (this will be called if the onchange event has been triggered)
506 function allowSaveChanges (element) {
507 // Mark element as changed, unmark as failed
508 $('#' + element).addClass('field_changed');
509 $('#' + element).removeClass('field_failed');
511 // Is the element not there?
512 if (!in_array(element, changedElements)) {
513 // Mark the form as changed
516 // Make the 'Save changes' button clickable
517 enableElement('#' + saveChangesId);
519 // Remember this for later AJAX call
520 changedElements.push(element);
524 // Marks all elements as unchanged/not failed
525 function markAllElementsAsUnchanged () {
526 // Remove status from all fields
527 for (var i = 0; i < changedElements.length; i++) {
528 // Mark the element as changed
529 $('#' + changedElements[i]).removeClass('field_changed');
530 $('#' + changedElements[i]).removeClass('field_failed');
534 // Function to reset the form
535 function resetMailerAjaxForm () {
537 //* DEBUG: */ alert('resetMailerAjaxForm(): changedElements()=' + changedElements.length + ',saveChangesId=' + saveChangesId + ' - ENTERED!');
539 // Mark all elements as unchanged
540 markAllElementsAsUnchanged();
542 // Clear all changed elements
543 disableElement('input#' + saveChangesId);
545 // Reset changed elements and mark form as not changed
546 changedElements = new Array();
550 // Mark given fields as failed
551 function markFormFieldsFailed (failedFields) {
552 // Mark all elements as failed
553 $.each(failedFields, function (i, field) {
554 // Mark the element as 'failed'
555 $('#' + field).removeClass('field_changed');
556 $('#' + field).addClass('field_failed');
558 // Also register it as 'changed', if not found
559 if (!in_array(field, changedElements)) {
560 // Okay, not yet added, so push it on the array
561 changedElements.push(field);
566 // Processes the content from AJAX call
567 function processAjaxResponseContent (prefix, ajax_content) {
568 // By default all is failed
569 var isResponseDone = false;
571 // Is 'status' and 'message' set?
572 if ((ajax_content.status == undefined) || (ajax_content.message == undefined)) {
573 // No status/message set
574 lastErrorMessage = 'Returned content does not provide $status$ or $message$ which is required.';
575 } else if ((ajax_content.status != 'done') && (ajax_content.status != 'failed')) {
576 // This is also not good, only 'failed' or 'done' is supported
577 lastErrorMessage = ajax_content.message + '<br />\nAdditionally an unknown status ' + ajax_content.status + ' was detected.';
578 } else if (ajax_content.status == 'failed') {
579 // Something bad went wrong so copy the message
580 lastErrorMessage = ajax_content.message;
583 isResponseDone = true;
587 return isResponseDone;
590 // Saves changes by sending the data to the AJAX backend script
591 function saveChanges (prefix) {
592 // Mark all elements as unchanged
593 markAllElementsAsUnchanged();
595 // Do we have changed elements
596 if (changedElements.length == 0) {
597 // This should not happen
598 displayErrorWindow(prefix, '<div class="ajax_error_message">saveChanges() called with no changed elements.</div>');
599 } else if (saveChangesId == null) {
600 // saveChangesId is not det
601 displayErrorWindow(prefix, '<div class="ajax_error_message">saveChangesId is not set. Please add <em>saveChanges = \'foo_bar\';</em> to your code.</div>');
604 // Serialize the whole form
605 var serializedData = $('form').serialize();
607 // Hold the ready status
609 saveChangesPending = true;
612 * Send the request to the AJAX backend, it doesn't matter from which page
613 * this was requested.
615 if (sendAjaxRequest(prefix, 'save_changes', '&tab=' + currentTabId + '&' + serializedData, true) == true) {
617 var ajax_content = getAjaxContent();
619 // Process the returned content
620 if (processAjaxResponseContent(prefix, ajax_content) == true) {
621 // Mark all elements as unchanged
622 markAllElementsAsUnchanged();
625 resetMailerAjaxForm();
627 // Do we have 'failed_fields' set?
628 if ((ajax_content.failed_fields != undefined) && (ajax_content.message != undefined)) {
629 // Mark all fields as 'failed'
630 markFormFieldsFailed(ajax_content.failed_fields);
632 // Display the error message
633 displayErrorWindow(prefix, '<div class="ajax_error_message">' + ajax_content.message + '</div>');
635 // This didn't work, why?
636 displayErrorWindow(prefix, '<div class="ajax_error_message">processAjaxResponseContent() failed, please fix this.<br />\n' + lastErrorMessage + '</div>');
640 // Saving changes may have worked
641 saveChangesPending = false;
643 // Mark all elements as unchanged
644 markAllElementsAsUnchanged();
646 // Display error message
647 displayErrorWindow(prefix, getAjaxContent());
649 // Saving changes didn't work
650 saveChangesPending = false;
653 // Wait for all has finished
657 // Waiting for resources being loaded
658 function saveChangesLocked () {
659 // Has all been loaded?
660 if (saveChangesPending == false) {
661 // Then release ready()
664 // Recursive call again
665 window.setTimeout('saveChangesLocked()', 10);
669 // Saves changed settings and continues with given page (next/previous)
670 function doSaveChangesPage (prefix, htmlId, page) {
675 //* DEBUG: */ alert('doSaveChangesPage(): prefix=' + prefix + ',htmlId=' + htmlId + ',page=' + page + ' - calling closeWarningWindow()');
676 closeWarningWindow(prefix, true, false);
678 // Load requested page
679 doFooterPage(prefix, htmlId, page);
682 // Saves changed settings and continues with given tab
683 function doSaveChangesContinue (prefix, htmlId, tab) {
688 //* DEBUG: */ alert('doSaveChangesPage(): prefix=' + prefix + ',htmlId=' + htmlId + ',tab=' + tab + ' - calling closeWarningWindow()');
689 closeWarningWindow(prefix, true, false);
691 // Load requested content
692 requestAjaxContent(prefix, htmlId, tab);