topical media & game development
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, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """)
} 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.