var Comments = { commentTemplate: null, commentRequests: {}, editingPostId: null, submittingPostId: null, init: function() { this.commentTemplate = new Template("commentTemplate"); if (window.location.hash == "#comments") { var firstBlogPost = getElementByClass(document.documentElement, "blogPost"); var postId = firstBlogPost.id.substr(4); this.toggle(postId); } }, onURLChange: function() { }, onClick: function(postId, event) { preventDefault(event); Comments.toggle(postId); }, //////////////////////////////////////////////////////////////////////////////////////////////// toggle: function(postId) { var postElt = $("post" + postId); var commentLinkBox = getElementByClass(postElt, "commentLinkBox"); if (hasClass(commentLinkBox, "toggled")) this.show(postId, true); else this.show(postId); }, show: function(postId, hide) { var postElt = $("post" + postId); var commentLinkBox = getElementByClass(postElt, "commentLinkBox"); if (hide) { removeClass(commentLinkBox, "toggled"); removeClass(commentLinkBox, "ready"); this.showEditor(postId, true); $("post" + postId + "Comments").style.display = "none"; } else { addClass(commentLinkBox, "toggled"); this.load(postId); } }, showEditor: function(postId, hide) { if (!hide && this.editingPostId) this.showEditor(this.editingPostId, true); if (!postId) postId = this.editingPostId; if (!postId) return; var postElt = $("post" + postId); var commentLinkBox = getElementByClass(postElt, "commentLinkBox"); if (!hide && hasClass(commentLinkBox, "editing")) return; var commentEditorBox = $("commentEditorBox"); if (hide) { commentEditorBox.style.display = "none"; removeClass(commentLinkBox, "editing"); this.editingPostId = null; } else { this.editingPostId = postId; $("commentEntryId").value = Number(postId); addClass(commentLinkBox, "editing"); commentEditorBox.style.display = "block"; $("commentEditor").value = ""; var commentsElt = $(postElt.id + "Comments"); commentsElt.parentNode.insertBefore(commentEditorBox, commentsElt); var emptyName = LabeledInput.apply($("commentName"), "Your Name"); var emptyEmail = LabeledInput.apply($("commentEmail"), "Your E-mail or URL"); $("commentEditor").focus(); smoothScrollIntoView(commentEditorBox); } }, //////////////////////////////////////////////////////////////////////////////////////////////// load: function(postId) { var postElt = $("post" + postId); var commentLinkBox = getElementByClass(postElt, "commentLinkBox"); addClass(commentLinkBox, "loading"); var url = "/blog/comments/" + postId + ".xml"; // Add the current time to the url to make it unique so that we skip the cache var args = new Date().getTime(); var req = loadXMLDocument(url, args, function(doc) { Comments.onLoad(doc, postId); }); this.commentRequests[postId] = req; }, cancelLoad: function(postId) { if (postId in this.commentRequests) { this.commentRequests[postId].abort(); delete this.commentRequests[postId]; } Comments.show(postId, true); }, onLoad: function(doc, postId) { delete this.commentRequests[postId]; var postElt = $("post" + postId); var commentLinkBox = getElementByClass(postElt, "commentLinkBox"); removeClass(commentLinkBox, "loading"); addClass(commentLinkBox, "ready"); var commentsBox = $("post" + postId + "Comments"); clearChildren(commentsBox); // Create and insert the comment elements var commentCount = 0; var items = doc.documentElement.childNodes; for (var i = 0; i < items.length; ++i) { if (items[i].nodeType == 1) { ++commentCount; var id = items[i].getAttribute("id"); var author = Comments.readXMLNodeValue(items[i], "author"); var email = Comments.readXMLNodeValue(items[i], "email"); var url = Comments.readXMLNodeValue(items[i], "url"); var timestamp = Comments.readXMLNodeValue(items[i], "timestamp"); var body = Comments.readXMLNodeValue(items[i], "text"); Comments.insertCommentBox(commentsBox, id, author, email, url, timestamp, body); } } commentsBox.style.display = "block"; if (commentCount == 0) this.showEditor(postId); else smoothScrollIntoView(commentsBox); }, insertCommentBox: function(commentsBox, id, author, email, url, timestamp, body) { var commentElt = this.commentTemplate.createTemplate(); commentElt.id = id; if (email) url = "mailto:" + email; // If no url/email is provided then don't put a link around the author name if (url) { var html = '' + author + ''; this.commentTemplate.setHTMLValue(commentElt, "author", html); } else this.commentTemplate.setTextValue(commentElt, "author", author); // Format the date to look pretty (no offense to all you ISO members out there) var theDate = new Date(timestamp); var dateFormatted = (theDate.getHours() > 11 ? theDate.getHours()-11 : theDate.getHours()) + (theDate.getHours() > 11 ? "pm" : "am"); this.commentTemplate.setTextValue(commentElt, "timestamp", dateFormatted); this.commentTemplate.setHTMLValue(commentElt, "content", body); commentsBox.insertBefore(commentElt, commentsBox.firstChild); if (commentsBox.childNodes.length % 2) setClass(commentElt, "commentBoxOdd"); return commentElt; }, //////////////////////////////////////////////////////////////////////////////////////////////// onSubmit: function(event) { var commentName = $("commentName"); var commentEditor = $("commentEditor"); var commentEmail = $("commentEmail"); if (!commentName.value || commentName.value == commentName.defaultValue) { alert("You need a name to comment on my website. Feel free to make one up."); commentName.focus(); event.preventDefault(); } else if (!commentEditor.value) { alert("Are you going to write something or not?"); commentEditor.focus(); event.preventDefault(); } else { this.submittingPostId = $("commentEntryId").value; var body = Comments.textToHTML(commentEditor.value); var commentsBox = $("post" + this.editingPostId + "Comments"); var commentBox = Comments.insertCommentBox(commentsBox, "", $("commentName").value, $("commentEmail").value, null, new Date(), body); this.showEditor(this.editingPostId, true); var commentProgress = $("commentEditorProgress"); commentProgress.style.display = "block"; commentBox.appendChild(commentProgress); var emailRegex = /(.*?)\@(.*?)\.(.*)/i; if (emailRegex.test(commentEmail.value)) commentEmail.name = "email"; else commentEmail.name = "url"; } }, onPosted: function(event) { if (this.submittingPostId) { var commentProgress = $("commentEditorProgress"); commentProgress.style.display = "none"; this.submittingPostId = null; $("formSubmitter").src = "about:blank"; } }, //////////////////////////////////////////////////////////////////////////////////////////////// textToHTML: function(text) { var lines = text.split("\n"); var html = ""; for (var i in lines) html += "

" + lines[i] + "

"; return html; }, readXMLNodeValue: function(node, sourceNodeName) { var sourceNode = node.getElementsByTagName(sourceNodeName)[0]; if (sourceNode.childNodes.length) return sourceNode.childNodes[0].nodeValue; else return ""; } }; //////////////////////////////////////////////////////////////////////////////////////////////////// var LabeledInput = { apply: function(inputElt, defaultValue) { addListener(inputElt, "focus", this.onFocusInput, true); addListener(inputElt, "blur", this.onBlurInput, true); inputElt.defaultValue = defaultValue; if (inputElt.value == "" || inputElt.value == defaultValue) { addClass(inputElt, "empty"); inputElt.value = defaultValue; return true; } return false; }, onFocusInput: function(event) { if (getEventTarget(event).nodeName == "INPUT" && hasClass(getEventTarget(event), "empty")) { getEventTarget(event).defaultValue = getEventTarget(event).value; getEventTarget(event).value = "" removeClass(getEventTarget(event), "empty"); } }, onBlurInput: function(event) { if (getEventTarget(event).nodeName == "INPUT" && getEventTarget(event).value == "") { getEventTarget(event).value = getEventTarget(event).defaultValue; addClass(getEventTarget(event), "empty"); } } };