function message(inParent, inID, inAccountID, inUsername, inTimeAgo, inMessage, inReplies) { this.idString = function() { // Generate id string for divs associated with this object if (this.isRoot()) return "root"; else return this.id; } // function idString this.isRoot = function() { return (typeof(this.parent) === "undefined") } // function isRoot this.increaseReplies = function() { // The number of replies isn't reported for the root if (this.isRoot()) return true; // Increase replies this.replies++; document.getElementById('discussion_msg' + this.idString() + '_numreplies').innerHTML = this.replies + ' replies'; } // function increaseReplies this.updateRepliesLeft = function(inRepliesLeft) { // Update replies this.repliesLeft = inRepliesLeft; // If there are no replies left hide the button if (inRepliesLeft <= 0) { document.getElementById('discussion_msg' + this.idString() + '_repliesleft').style.display = 'none'; } // Otherwise display the button else { if (this.isRoot()) document.getElementById('discussion_msg' + this.idString() + '_repliesleft').innerHTML = 'load more messages (' + inRepliesLeft + ')'; else document.getElementById('discussion_msg' + this.idString() + '_repliesleft').innerHTML = 'load more replies (' + inRepliesLeft + ')'; } } // function updateRepliesLeft this.hidePostingReply = function() { document.getElementById('discussion_msg' + this.idString() + '_postingreply').style.display = 'none'; } this.showPostingReply = function() { document.getElementById('discussion_msg' + this.idString() + '_postingreply').style.display = 'block'; } this.hideLoadingReplies = function() { document.getElementById('discussion_msg' + this.idString() + '_loadingreplies').style.display = 'none'; } this.showLoadingReplies = function() { document.getElementById('discussion_msg' + this.idString() + '_loadingreplies').style.display = 'block'; } this.loadChildren = function(inStartAfterMessageID, inEndOnMessageID) { // Say loading this.showLoadingReplies(); // Set up http request object var http; if (XMLHttpRequest) http = new XMLHttpRequest(); else if (ActiveXObject) http = new ActiveXObject("Microsoft.XMLHTTP"); // Pass the message ID if this isn't the root var paramMessageID = ''; if (!this.isRoot()) paramMessageID = '&mid=' + this.id; // Are we starting after a message id? var paramStartAfter = ''; if (typeof(inStartAfterMessageID) !== 'undefined') paramStartAfter = '&startafter=' + inStartAfterMessageID; // Are we ending on a message id? var paramEndOn = ''; if (typeof(inStartAfterMessageID) !== 'undefined') paramEndOn = '&endon=' + inEndOnMessageID; // Request data http.open('GET', 'http://' + domain() + '/ajax/discussion_getreplies.php?did=' + globalDiscussionID + paramMessageID + paramStartAfter + paramEndOn); http.onreadystatechange = function (inParentID) { return function() { // When ready with data if (http.readyState == 4) { // Get the parent message object (needed even if error to hide loading text) parentMessage = globalRootMessage; if (inParentID != null) parentMessage = globalRootMessage.getMessageByID(inParentID); // Success if (http.status == 200) { // Check for an error var error = http.responseXML.getElementsByTagName("error"); if (error.item(0) != null) { alert('Error: ' + error.item(0).childNodes[0].nodeValue); } else { // Load XML and determine which message the replies are for var root = http.responseXML.getElementsByTagName("replies").item(0); var parentMessage; // Add each message for (var i = 0; i < root.getElementsByTagName("reply").length; i++) { // Create the div for this message document.getElementById('discussion_msg' + parentMessage.idString() + '_replies').innerHTML += '
'; // Create the message object, which sets up the html parentMessage.children.push( new message( parentMessage, root.getElementsByTagName("reply").item(i).getAttribute('id'), root.getElementsByTagName("reply").item(i).getAttribute('accountid'), root.getElementsByTagName("reply").item(i).getAttribute('username'), root.getElementsByTagName("reply").item(i).getAttribute('timeago'), root.getElementsByTagName("reply").item(i).childNodes[0].nodeValue, root.getElementsByTagName("reply").item(i).getAttribute('replies') )); // Pulse the child parentMessage.children[parentMessage.children.length - 1].pulseTitle(); } // Mark some messages as loaded parentMessage.loadedSomeReplies = true; // Set the number of messages remaining to load parentMessage.updateRepliesLeft(root.getAttribute('repliesleft')); // If this is part of an initial load sequence then keep going or display the messages if (typeof(window.globalMessageLoadSequence) !== 'undefined' && window.globalMessageLoadSequence.length > 0) { // Check that the message was loaded properly var curMID = window.globalMessageLoadSequence.shift(); if (!globalRootMessage.getMessageByID(curMID)) { alert('Error loading message.'); return false; } // Show the replies globalRootMessage.getMessageByID(curMID).parent.showReplies(); // No problem -- show the messages or load the next message if (window.globalMessageLoadSequence.length > 0) { globalRootMessage.getMessageByID(curMID).loadChildren(null, window.globalMessageLoadSequence[0]); } else { document.getElementById('discussion_loading').style.display = 'none'; document.getElementById('discussion_msgroot').style.display = 'block'; globalRootMessage.getMessageByID(curMID).highlight(); globalRootMessage.getMessageByID(curMID).scrollTo(); globalPulsingEnabled = true; } } else { // Initial load over -- allow pulsing globalPulsingEnabled = true; } } } // Failure else { alert('Error retrieving messages.'); } // Hide loading icon parentMessage.hideLoadingReplies(); // Check if there are any messages and if not show "no messages" globalRootMessage.updateNoMessagesText(); } }}(this.id); // Pass parameters to return function http.send(null); } // function loadChildren this.updateNoMessagesText = function() { // Check if there are any messages and if not show "no messages" if (globalRootMessage.children.length == 0) document.getElementById('discussion_empty').style.display = 'block'; else document.getElementById('discussion_empty').style.display = 'none'; } // function updateNoMessagesText this.setHTML = function() { // If this is the root then over-ride the margin-left style var marginText = ''; if (this.isRoot()) marginText = ' style="margin-left: 0;"'; // Set HMTL: anchor name buildHTML = ''; buildHTML += ''; // Set HTML: title if (!this.isRoot()) { buildHTML += '
'; buildHTML += '' + this.username + ' '; buildHTML += '' + this.timeAgo + ' '; buildHTML += '
'; } // Set HTML: message if (!this.isRoot()) { buildHTML += '
' + this.message + '
'; } // Set HTML: actions buildHTML += '
'; if (!this.isRoot()) { // Highlight the number of replies if there are some var numRepliesStyle = ''; if (this.replies > 0) numRepliesStyle = 'border-bottom: 1px solid #900;'; // Add to HTML buildHTML += '' + this.replies + ' replies'; if (globalLoggedIn) { buildHTML += 'reply'; } } else { if (globalLoggedIn) { buildHTML += 'post message'; } else { buildHTML += 'You must be logged in to post a message.'; } } buildHTML += '
'; // Set HTML: replyform if (globalLoggedIn) { buildHTML += ''; buildHTML += '
' + globalUsername + ' 0 minutes ago
'; buildHTML += '
'; buildHTML += '
'; buildHTML += 'Post'; buildHTML += 'Cancel'; buildHTML += '
'; buildHTML += ''; } // Set HTML: postingreply buildHTML += ''; buildHTML += '
Posting reply...
'; buildHTML += '
'; buildHTML += ''; // Set HTML: replies buildHTML += ''; // Set HTML: loadingreplies buildHTML += ''; buildHTML += '
Loading messages...
'; buildHTML += '
'; buildHTML += ''; // Set HTML: morereplies buildHTML += ''; if (this.isRoot()) { buildHTML += ''; } else { buildHTML += ''; } buildHTML += ''; // Set HTML document.getElementById('discussion_msg' + this.idString()).innerHTML = buildHTML; } this.postReply = function() { // Check if a reply was entered if (document.getElementById("discussion_msg" + this.idString() + "_replytext").value == '') { alert('Please enter a message.'); document.getElementById("discussion_msg" + this.idString() + "_replytext").focus(); return false; } // Say posting reply this.showPostingReply(); // Set up http request object var http; if (XMLHttpRequest) http = new XMLHttpRequest(); else if (ActiveXObject) http = new ActiveXObject("Microsoft.XMLHTTP"); // Set up parameters var params = 'did=' + globalDiscussionID; if (!this.isRoot()) params += '&pid=' + this.id; params += '&message=' + encodeURIComponent(document.getElementById("discussion_msg" + this.idString() + "_replytext").value); // Request http.open("POST", 'http://' + domain() + '/ajax/discussion_post.php', true); http.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); http.setRequestHeader("Content-length", params.length); http.setRequestHeader("Connection", "close"); http.onreadystatechange = function (inParentID, inReplyText) { return function() { // When ready with data if (http.readyState == 4) { // Get the parent message object (needed even if error to hide loading text) var parentMessage = globalRootMessage; if (inParentID != null) parentMessage = globalRootMessage.getMessageByID(inParentID); // Success if (http.status == 200) { // Check for an error var error = http.responseXML.getElementsByTagName("error"); if (error.item(0) != null) { alert('Error: ' + error.item(0).childNodes[0].nodeValue); } else { // Load XML and determine which message the reply is for var root = http.responseXML.getElementsByTagName("success").item(0); var newMessageID = root.getAttribute("id"); // Create the div for this message at the beginning of the container document.getElementById('discussion_msg' + parentMessage.idString() + '_replies').innerHTML = '
' + document.getElementById('discussion_msg' + parentMessage.idString() + '_replies').innerHTML; // Add the object to the beginning of children array (creates its own html) for (var i = parentMessage.children.length; i > 0; i--) parentMessage.children[i] = parentMessage.children[i-1] parentMessage.children[0] = new message( parentMessage, newMessageID, globalAccountID, globalUsername, '0 minutes ago', htmlspecialchars(inReplyText), 0 ); // Increase the number of replies parentMessage.increaseReplies(); // Pulse reply title or parent num replies depending on whether replies are visible if (document.getElementById('discussion_msg' + parentMessage.idString() + '_replies').style.display != 'block') parentMessage.pulseNumReplies(); else parentMessage.children[0].pulseTitle(); } } // Failure else { alert('Error posting message.'); } // Check if there are any messages and if not show "no messages" globalRootMessage.updateNoMessagesText(); // Hide 'posting reply' message parentMessage.hidePostingReply(); } } }(this.id, document.getElementById("discussion_msg" + this.idString() + "_replytext").value); // Pass parameters to onreadystatechange http.send(params); // Reset and hide the reply form document.getElementById("discussion_msg" + this.idString() + "_replytext").value = ''; this.hideReplyForm(false); } // function postReply this.loadMoreReplies = function() { this.loadChildren(this.children[this.children.length-1].id); } // function loadMoreReplies this.toggleReplyForm = function(inPulse) { // Switch visibility if (document.getElementById('discussion_msg' + this.idString() + '_replyform').style.display != 'block') this.showReplyForm(inPulse); else this.hideReplyForm(inPulse); } // function toggleReplyForm this.showReplyForm = function(inPulse) { // Switch visibility document.getElementById('discussion_msg' + this.idString() + '_replyform').style.display = 'block'; // Set focus document.getElementById('discussion_msg' + this.idString() + '_replytext').focus(); // Pulse the reply form title if (typeof(inPulse) !== 'undefined' && inPulse == true) this.pulseReplyFormTitle(); } // function showReplyForm this.hideReplyForm = function(inPulse) { // Switch visibility document.getElementById('discussion_msg' + this.idString() + '_replyform').style.display = 'none'; // Pulse title if (typeof(inPulse) !== 'undefined' && inPulse == true) this.pulseTitle(); } // function hideReplyForm this.showReplies = function() { // If there are no replies then abort // Except root, since we don't know how many replies there are if (this.replies <= 0 && !this.isRoot()) return true; // Load children if necessary if (!this.loadedSomeReplies) this.loadChildren(); // Show replies and "more replies" button var replies = document.getElementById('discussion_msg' + this.idString() + '_replies'); var moreReplies = document.getElementById('discussion_msg' + this.idString() + '_morereplies'); replies.style.display = 'block'; moreReplies.style.display = 'block'; // Pulse all the replies for (i = 0; i < this.children.length; i++) this.children[i].pulseTitle(); } // function showReplies this.hideReplies = function() { var replies = document.getElementById('discussion_msg' + this.idString() + '_replies'); var moreReplies = document.getElementById('discussion_msg' + this.idString() + '_morereplies'); replies.style.display = 'none'; moreReplies.style.display = 'none'; this.pulseTitle(); } // function hideReplies this.toggleReplies = function() { if (document.getElementById('discussion_msg' + this.idString() + '_replies').style.display != 'block') { this.showReplies(); } else { this.hideReplies(); } } // function toggleReplies this.getMessageByID = function(inMessageID) { // If this is the message return self, otherwise recurse on children if (inMessageID == this.id || (inMessageID == 'root' && this.isRoot())) { return this; } else { for (key in this.children) { var res = this.children[key].getMessageByID(inMessageID); if (res) return res; } } return false; } // function getMessageByID this.loadDescendentByID = function(inID) { // Hide the discussion and show the loading notice document.getElementById('discussion_loading').style.display = 'block'; document.getElementById('discussion_msgroot').style.display = 'none'; // Set up http request object to get ancestor IDs var http; if (XMLHttpRequest) http = new XMLHttpRequest(); else if (ActiveXObject) http = new ActiveXObject("Microsoft.XMLHTTP"); // Request data http.open('GET', 'http://' + domain() + '/ajax/discussion_getancestorids.php?mid=' + inID); http.onreadystatechange = function () { // When ready with data if (http.readyState == 4) { // Success if (http.status == 200) { // Load XML and cycle through ancestor ids var root = http.responseXML.getElementsByTagName("ids").item(0); // Add each ancestor id window.globalMessageLoadSequence = []; for (var i = 0; i < root.getElementsByTagName("id").length; i++) window.globalMessageLoadSequence.push(root.getElementsByTagName("id").item(i).childNodes[0].nodeValue); window.globalMessageLoadSequence.push(inID); // Start loading the first message globalRootMessage.loadChildren(null, window.globalMessageLoadSequence[0]); } // Failure else { // Alert error alert('Error displaying message.'); // Hide loading display globalRootMessage.updateNoMessagesText(); document.getElementById('discussion_loading').style.display = 'none'; document.getElementById('discussion_msgroot').style.display = 'block'; } } } http.send(null); } // function loadDescendentByID this.scrollTo = function() { // curLocation = window.location.href; // window.location = curLocation.substring(0, curLocation.indexOf('#') - 1) + '#msg_' + this.idString(); window.location.href = '#msg_' + this.idString(); } // function scrollTo this.highlight = function() { document.getElementById('discussion_msg' + this.idString() + '_title').style.backgroundColor = '#161616'; } // function highlight this.pulseTitle = function() { // No pulsing the root if (!this.isRoot() && globalPulsingEnabled == true) pulse('discussion_msg' + this.idString() + '_title'); } // function pulseTitle this.pulseReplyFormTitle = function() { if (globalPulsingEnabled == true) pulse('discussion_msg' + this.idString() + '_replyformtitle'); } // function pulseReplyFormTitle this.pulseNumReplies = function() { if (globalPulsingEnabled == true) pulse('discussion_msg' + this.idString() + '_numreplies'); } // function pulseNumReplies // Constructor this.children = []; this.loadedSomeReplies = false; // These variables are null for the root message object if (typeof(inParent) !== "undefined") { this.parent = inParent; this.id = inID; this.accountID = inAccountID; this.username = inUsername; this.timeAgo = inTimeAgo; this.message = inMessage; // This will contain markdown HTML and no user HTML -- do not escape this.replies = inReplies; } // Set up the HTML this.setHTML(); } function pulse(inDivID) { pulseIteration(inDivID, 1); } // function pulse function pulseIteration(inDivID, inIterationNumber) { // Set max iterations var maxIterations = 35; // Set the new color var rgb = Math.round(35 * (1 - inIterationNumber / maxIterations) + 3); var bgColor = 'rgb(' + rgb + ', ' + rgb + ', ' + rgb + ')'; document.getElementById(inDivID).style.backgroundColor = bgColor; // Continue iterating if necessary or reset background color if (inIterationNumber < maxIterations) setTimeout('pulseIteration("' + inDivID + '", ' + (inIterationNumber + 1) + ');', 50); else document.getElementById(inDivID).style.backgroundColor = ''; } // function pulseIteration