topical media & game development

talk show tell print

professional-ajax-09-AjaxMail-scripts-AjaxMail.js / js



  
  //URLs
  var sAjaxMailURL = <AjaxMailAction.php>;
  var sAjaxMailNavigateURL = <AjaxMailNavigate.php>;
  var sAjaxMailAttachmentURL = <AjaxMailAttachment.php>;
  var sAjaxMailSendURL = <AjaxMailSend.php>;
  var sImagesDir = "images/";
  var sRestoreIcon = sImagesDir + "icon_restore.gif";
  var sDeleteIcon = sImagesDir + "icon_delete.gif";
  var sInfoIcon = sImagesDir + "icon_info.gif";
  var sErrorIcon = sImagesDir + "icon_alert.gif";
  var aPreloadImages = [sRestoreIcon, sDeleteIcon, sInfoIcon, sErrorIcon];
  
  //Strings
  var sEmptyTrashConfirm = "You are about to permanently delete everything in the Trash. Continue?";
  var sEmptyTrashNotice = "The Trash has been emptied.";
  var sDeleteMailNotice = "The message has been moved to Trash.";
  var sRestoreMailNotice = "The message has been moved to Inbox.";
  var sTo = "To ";
  var sCC = "CC ";
  var sBCC = "BCC ";
  var sFrom = "From ";
  var sRestore = "Restore";
  var sDelete = "Move to Trash";
  
  //Timeout Settings
  var iShowNoticeTime = 5000;
  
  //Folders
  var INBOX = 1;
  var TRASH = 2;
  var aFolders = ["","Inbox", "Trash"];
  
  //preload the images
  for (var i=0; i < aPreloadImages.length; i++) {
      var oImg = new Image();
      oImg.src = aPreloadImages[i];
  }
  
  
The mailbox.

  
  var oMailbox = {
  
      //-----------------------------------------------------
      // Properties
      //-----------------------------------------------------
      
      //folder-related information
      info: new Object(),   //information about the mail being displayed
      processing: false,    //determines if processing is taking place
      message: new Object(),//information about the current message    
      nextNotice: null,    //information to be displayed to the user
  
      //-----------------------------------------------------
      // Data-Related Methods
      //-----------------------------------------------------
      
      
Moves a message to the trash. @scope protected
parameter: sId The ID of the message.

  
      deleteMessage: function (sId) {
          this.nextNotice = sDeleteMailNotice;
          this.request("delete", loadAndRender, sId);        
      },
      
      
Moves a message to the trash. @scope protected
parameter: sId The ID of the message.

  
      emptyTrash: function () {
          if (confirm(sEmptyTrashConfirm)) {
              this.nextNotice = sEmptyTrashNotice;
              if (this.info.folder == TRASH) {
                  this.request("empty", loadAndRender);          
              } else {
                  this.request("empty", execute);
              }      
          }
      },
      
      
Retrieves messages for the current folder and page. @scope protected
parameter: iFolder The folder to retrieve.
parameter: iPage The page in that folder to retrieve.

  
      getMessages: function (iFolder, iPage) {
          this.info.folder = iFolder;
          this.info.page = iPage;
          this.navigate("getfolder");
      },    
              
      
Loads data from the server into the mailbox. @scope protected
parameter: vInfo A JSON-encoded string containing mailbox information or an info object.

  
      loadInfo: function (vInfo) {
          if (typeof vInfo == "string") {
              this.info = JSON.parse(vInfo);  
          } else {
              this.info = vInfo;
          }  
      },    
      
      
Loads message data from the server into the mailbox. @scope protected
parameter: vMessage A JSON-encoded string containing message information or a message object.

  
      loadMessage: function (vMessage) {
          if (typeof vMessage == "string") {
              this.message = JSON.parse(vMessage);  
          } else {
              this.message = vMessage;
          }  
      },       
      
      
Makes a request to the server. @scope protected
parameter: sAction The action to perform.
parameter: fnCallback The function to call when the request completes.
parameter: sId The ID of the message to act on (optional).

  
      navigate: function (sAction, sId) {
          if (this.processing) return;
          try {
              this.setProcessing(true);
              var sURL = sAjaxMailNavigateURL + "?folder=" +this.info.folder + "&page=" + this.info.page + "&action=" + sAction;
              if (sId) {
                  sURL += "&id=" + sId;
              }
              this.iLoader.src = sURL;
          } catch (oException) {
              this.showNotice("error", oException.message);
          }
      },        
      
      
Retrieves messages for the next page in the current folder. @scope protected
parameter: iFolder The folder to retrieve.
parameter: iPage The page in that folder to retrieve.

  
      nextPage: function () {
          this.getMessages(this.info.folder, this.info.page+1);
      },
          
      
Retrieves messages for the previous page in the current folder. @scope protected
parameter: iFolder The folder to retrieve.
parameter: iPage The page in that folder to retrieve.

  
      prevPage: function () {
          this.getMessages(this.info.folder, this.info.page-1);
      },   
      
      
Begins download of the given message.
parameter: sId The message ID to retrieve.

  
      readMessage: function (sId) {
          this.navigate("getmessage", sId);
      },    
      
      
Refreshes the current folder's view. @scope protected
parameter: iFolder The ID of the new folder to refresh.

  
      refreshFolder: function (iFolder) {
          this.info.folder = iFolder;
          this.info.page = 1;
          this.request("getfolder", loadAndRender);     
      },        
      
      
Makes a request to the server. @scope protected
parameter: sAction The action to perform.
parameter: fnCallback The function to call when the request completes.
parameter: sId The ID of the message to act on (optional).

  
      request: function (sAction, fnCallback, sId) {
          if (this.processing) return;
          try {
              this.setProcessing(true);
              var oXmlHttp = zXmlHttp.createRequest();
              var sURL = sAjaxMailURL + "?folder=" +this.info.folder + "&page=" + this.info.page + "&action=" + sAction;
              if (sId) {
                  sURL += "&id=" + sId;
              }
  
              oXmlHttp.open("get", sURL, true);
              oXmlHttp.onreadystatechange = function (){
                  try {
                      if (oXmlHttp.readyState == 4) {
                          if (oXmlHttp.status == 200) {   
                              fnCallback(oXmlHttp.responseText);                     
                          } else {
                              throw new Error("An error occurred while attempting to contact the server. The action (" + sAction + ") did not complete.");
                          }
                      }
                  } catch (oException) {
                      oMailbox.showNotice("error", oException.message);
                  }
              };
              oXmlHttp.send(null);
          } catch (oException) {
              this.showNotice("error", oException.message);
          }
      },            
          
      
Moves a message from the trash to the inbox. @scope protected
parameter: sId The ID of the message.

  
      restoreMessage: function (sId) {
          this.nextNotice = sRestoreMailNotice;
          this.request("restore", loadAndRender, sId);        
      },  
      
      
Makes a request to the server. @scope protected
parameter: sAction The action to perform.
parameter: fnCallback The function to call when the request completes.
parameter: sId The ID of the message to act on (optional).

  
      sendMail: function () {
          if (this.processing) return;
          this.divComposeMailForm.style.display  = "none";
          this.divComposeMailStatus.style.display = "block";
          
          try {
              this.setProcessing(true);
              var oXmlHttp = zXmlHttp.createRequest();
              var sData = getRequestBody(document.forms["frmSendMail"]);
  
              oXmlHttp.open("post", sAjaxMailSendURL, true);
              oXmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
                   
              oXmlHttp.onreadystatechange = function (){
                  try {
                      if (oXmlHttp.readyState == 4) {
                          if (oXmlHttp.status == 200) {   
                              sendConfirmation(oXmlHttp.responseText);                     
                          } else {
                              throw new Error("An error occurred while attempting to contact the server. The mail was not sent.");
                          }
                      }
                  } catch (oException) {
                      oMailbox.showNotice("error", oException.message);
                  }
              };
              oXmlHttp.send(sData);
          } catch (oException) {
              this.showNotice("error", oException.message);
          }
      },              
      
      
Sets the UI to be enabled or disabled. @scope protected
parameter: bProcessing True to enable, false to disable.

  
      setProcessing: function (bProcessing) {
          this.processing = bProcessing;
          this.divFolderStatus.style.display = bProcessing ? "block" : "none";
      },
          
      
Switches the view to a new folder. @scope protected
parameter: iNewFolder The ID of the new folder to switch to.

  
      switchFolder: function (iNewFolder) {
          this.getMessages(iNewFolder, 1);        
      },        
          
      //-----------------------------------------------------
      // UI-Related Methods
      //-----------------------------------------------------
          
      
Cancels the reply and sends back to read mail view. @scope protected

  
      cancelReply: function () {
          history.go(-1);
      },    
              
      compose: function () {
          this.navigate("compose");
      },    
      
      displayCompose: function () {
          this.displayComposeMailForm("", "", "", ""); 
      },
      
      displayComposeMailForm: function (sTo, sCC, sSubject, sMessage) {
          this.txtTo.value = sTo;
          this.txtCC.value = sCC;        
          this.txtSubject.value = sSubject;
          this.txtMessage.value = sMessage;
          this.divReadMail.style.display = "none";
          this.divComposeMail.style.display = "block";     
          this.divFolder.style.display = "none";
          this.setProcessing(false);        
      },
          
      displayFolder: function (oInfo) {
          this.loadInfo(oInfo);
          this.renderFolder();
          this.setProcessing(false);
      },
      
      displayForward: function () {
          this.displayComposeMailForm("", "", 
                       "Fwd: " + this.message.subject,
                       "---------- Forwarded message ----------\n" + this.message.message);   
      },    
      
      displayMessage: function (oMessage) {
          this.loadMessage(oMessage);        
          this.renderMessage();
          this.setProcessing(false);
      },
     
      displayReply: function () {
          var sTo = this.message.from;
          var sCC = "";
      
          this.displayComposeMailForm(sTo, sCC, "Re: " + this.message.subject,
              "\n\n\n\n\n" + this.message.from + "said: \n" + this.message.message);
      
      },
      
      displayReplyAll: function () {
          var sTo = this.message.from + "," + this.message.to;
          var sCC = this.message.cc;
      
          this.displayComposeMailForm(sTo, sCC, "Re: " + this.message.subject,
              "\n\n\n\n\n" + this.message.from + "said: \n" + this.message.message);
      
      },
      
      forward: function () {
          this.navigate("forward");
      },
      
      
Initializes DOM pointers and other property values. @scope protected

  
      init: function () {
          var colAllElements = document.getElementsByTagName("*");
          if (colAllElements.length == 0) {
              colAllElements = document.all;
          }
          
          for (var i=0; i < colAllElements.length; i++) {
              if (colAllElements[i].id.length > 0) {
                  this[colAllElements[i].id] = colAllElements[i];
              }
          }
           
          //assign event handlers        
          this.imgPrev.onclick = function () {
              oMailbox.prevPage();
          };
          
          this.imgNext.onclick = function () {
              oMailbox.nextPage();
          };
          
          this.spnCompose.onclick = function () {
              oMailbox.compose();
          };
          
          this.spnInbox.onclick = function () {
              if (oMailbox.info.folder == INBOX) {
                  oMailbox.refreshFolder(INBOX);
              } else {
                  oMailbox.switchFolder(INBOX);
              }
          };
          
          this.spnTrash.onclick = function () {
              if (oMailbox.info.folder == TRASH) {
                  oMailbox.refreshFolder(TRASH);
              } else {
                  oMailbox.switchFolder(TRASH);
              }
          };        
          this.spnEmpty.onclick = function () {
              oMailbox.emptyTrash();
          };        
          this.spnReply.onclick = function () {
              oMailbox.reply(false);
          };        
          this.spnReplyAll.onclick = function () {
              oMailbox.reply(true);
          };        
          this.spnForward.onclick = function () {
              oMailbox.forward();
          };
          this.spnCancel.onclick = function () {
              oMailbox.cancelReply();
          };        
          this.spnSend.onclick = function () {
              oMailbox.sendMail();
          };
      },
          
      
Initializes and loads the mailbox with the initial page. @scope protected

  
      load: function () {
          this.init();
          this.getMessages(INBOX, 1);        
      },
      
      
Renders the messages on the screen. @scope protected

  
      renderFolder: function () {;
          var tblMain = this.tblMain;
  
          //remove all child nodes
          while (tblMain.tBodies[0].hasChildNodes()) {
              tblMain.tBodies[0].removeChild(tblMain.tBodies[0].firstChild);
          }                
  
          //create document fragment to store new DOM objects
          var oFragment = document.createDocumentFragment();
  
          //add a new row for each message
          if (this.info.messages.length) {
              for (var i=0; i < this.info.messages.length; i++) {
                  var oMessage = this.info.messages[i];
                  var oNewTR = this.trTemplate.cloneNode(true);
                  oNewTR.id = "tr" + oMessage.id;
                  oNewTR.onclick = readMail;
                  
                  if (oMessage.unread) {
                      oNewTR.className = "new";
                  }
                  
                  var colCells = oNewTR.getElementsByTagName("td");
                  var imgAction = colCells[0].childNodes[0];
                  imgAction.id = oMessage.id;
                  if (this.info.folder == TRASH) {
                      imgAction.onclick = restoreMail;
                      imgAction.src = sRestoreIcon;
                      imgAction.title = sRestore;
                  } else {
                      imgAction.onclick = deleteMail;
                      imgAction.src = sDeleteIcon;
                      imgAction.title = sDelete;
                  }
                  
                  colCells[1].appendChild(document.createTextNode(cleanupEmail(oMessage.from)));
                  colCells[2].firstChild.style.visibility = oMessage.hasAttachments ? "visible" : "hidden";
                  colCells[3].appendChild(document.createTextNode(htmlEncode(oMessage.subject)));
                  colCells[4].appendChild(document.createTextNode(oMessage.date));
                  oFragment.appendChild(oNewTR);           
              }
          } else {
              var oNewTR = this.trNoMessages.cloneNode(true);
              oFragment.appendChild(oNewTR);
          }
  
          //add the message rows
          tblMain.tBodies[0].appendChild(oFragment);
          
          //only change folder name if it's different
          if (this.hFolderTitle.innerHTML != aFolders[this.info.folder]) {
              this.hFolderTitle.innerHTML = aFolders[this.info.folder];
          }
          
          //update unread message count for Inbox
          this.updateUnreadCount(this.info.unreadCount);
               
          //set up the message count (hide if there are no messages)
          this.spnItems.style.visibility = this.info.messages.length ? "visible" : "hidden";
          this.spnItems.innerHTML = this.info.firstMessage + "-" + (this.info.firstMessage + this.info.messages.length - 1) + " of " + this.info.messageCount;
  
          //determine show/hide of pagination images
          if (this.info.pageCount > 1) {
              this.imgNext.style.visibility = this.info.page < this.info.pageCount ? "visible" : "hidden";
              this.imgPrev.style.visibility = this.info.page > 1 ? "visible" : "hidden";
          } else {
              this.imgNext.style.visibility = "hidden";
              this.imgPrev.style.visibility = "hidden";
          }              
          
          this.divFolder.style.display = "block";
          this.divReadMail.style.display = "none";        
          this.divComposeMail.style.display = "none";        
      },
      
      renderMessage: function () {
          this.hSubject.innerHTML = htmlEncode(this.message.subject);
          this.divMessageFrom.innerHTML = sFrom + " " + htmlEncode(this.message.from);
          this.divMessageTo.innerHTML = sTo + " " + htmlEncode(this.message.to);
          this.divMessageCC.innerHTML = this.message.cc.length ? sCC + " " + htmlEncode(this.message.cc) : "";
          this.divMessageBCC.innerHTML = this.message.bcc.length ? sBCC + " " + htmlEncode(this.message.bcc) : "";
          this.divMessageDate.innerHTML = this.message.date;
          this.divMessageBody.innerHTML = this.message.message;
          
          if (this.message.hasAttachments) {
              this.ulAttachments.style.display = "";
              
              var oFragment = document.createDocumentFragment();
              
              for (var i=0; i < this.message.attachments.length; i++) {
                  var oLI = document.createElement("li");
                  oLI.className = "attachment";
                  oLI.innerHTML = "<a href=\"" + sAjaxMailAttachmentURL + "?id=" + this.message.attachments[i].id + "\" target=\"_blank\">" + this.message.attachments[i].filename + "</a> (" + this.message.attachments[i].size + ")";
                  oFragment.appendChild(oLI);
              }
              
              this.ulAttachments.appendChild(oFragment);
              this.liAttachments.style.display = "";
          } else {
              this.ulAttachments.style.display = "none";
              this.liAttachments.style.display = "none";
              this.ulAttachments.innerHTML = "";
          }
          
          this.updateUnreadCount(this.message.unreadCount);
          this.divFolder.style.display = "none";
          this.divReadMail.style.display = "block";
          this.divComposeMail.style.display = "none";
      },
      
      
Sets up the screen to reply to an e-mail. @scope protected
parameter: blnAll Set to true for "reply to all"

  
      reply: function (blnAll) {
          this.navigate("reply" + (blnAll ? "all" : ""));
      },
      
      
Shows a message on the screen. @scope protected
parameter: sType The type of message to display.
parameter: sMessage The message to display.

  
      showNotice: function (sType, sMessage) {
          var divNotice = this.divNotice;
          divNotice.className = sType;
          divNotice.innerHTML = sMessage;
          divNotice.style.visibility = "visible";
          setTimeout(function () {
              divNotice.style.visibility = "hidden";
          }, iShowNoticeTime);    
      },
      
      
Updates the count of unread messages displayed on the screen. @scope protected
parameter: iCount The number of unread messages.

  
      updateUnreadCount: function (iCount) {
          this.spnUnreadMail.innerHTML = iCount > 0 ? " (" + iCount + ")" : "";
      }
  };
  
  /*-------------------------------------------------------
   * Callback Functions
   *-------------------------------------------------------*/
  
  
Callback function to execute a client-server request and display a notification about the result. @scope protected.
parameter: sInfo The information returned from the server.

  
  function execute(sInfo) {
      if (oMailbox.nextNotice) {
          oMailbox.showNotice("info", oMailbox.nextNotice);
          oMailbox.nextNotice = null;
      }
      oMailbox.setProcessing(false);
  }
  
  
Callback function to execute a client-server request and then load and display new mail information. @scope protected.
parameter: sInfo The information returned from the server.

  
  function loadAndRender(sInfo) {
  
      oMailbox.loadInfo(sInfo);
      oMailbox.renderFolder();
      
      if (oMailbox.nextNotice) {
          oMailbox.showNotice("info", oMailbox.nextNotice);
          oMailbox.nextNotice = null;
      }
      oMailbox.setProcessing(false);        
  }
  
  
The callback function when attempting to send an e-mail. @scope protected
parameter: sData The data returned from the server.

  
  function sendConfirmation(sData) {
      var oResponse = JSON.parse(sData);
      if (oResponse.error) {
          alert("An error occurred:\n" + oResponse.message);
      } else {
          oMailbox.showNotice("info", oResponse.message);
          oMailbox.divComposeMail.style.display = "none";
          oMailbox.divReadMail.style.display = "none";
          oMailbox.divFolder.style.display = "block";
      }
      oMailbox.divComposeMailForm.style.display  = "block";
      oMailbox.divComposeMailStatus.style.display = "none";        
      oMailbox.setProcessing(false);
  }
  
  /*-------------------------------------------------------
   * Event Handlers and Function Pointers
   *-------------------------------------------------------*/
  
  function deleteMail() {
      oMailbox.deleteMessage(this.id); 
  }
  
  function restoreMail() {
      oMailbox.restoreMessage(this.id); 
  }
  
  function readMail() {
      oMailbox.readMessage(this.id.substring(2));
  }
  
  /*-------------------------------------------------------
   * Helper Functions/Data
   *-------------------------------------------------------*/
  
  var reNameAndEmail = /(.*?)<(.*?)>/i;
  
  function cleanupEmail(sText) {
      if (reNameAndEmail.test(sText)) {
          return RegExp.$1.replace(/"/g, "");
      } else {
          return sText;
      }
  }
  
  function htmlEncode(sText) {
      if (sText) {
          return sText.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;")
      } else {
          return "";
      }
  }
  
  function getRequestBody(oForm) {
      var aParams = new Array();
      
      for (var i=0 ; i < oForm.elements.length; i++) {
          var sParam = encodeURIComponent(oForm.elements[i].name);
          sParam += "=";
          sParam += encodeURIComponent(oForm.elements[i].value);
          aParams.push(sParam);
      } 
      
      return aParams.join("&");        
  }
          
  //assign the mailbox to load when the page has completed loading
  window.onload = function () {
      oMailbox.load();
  };


(C) Æliens 20/2/2008

You may not copy or print any of this material without explicit permission of the author or the publisher. In case of other copyright issues, contact the author.