/** * Copyright 2009 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS-IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ var padchat = (function(){ var numToAuthorMap = ['']; var authorColorArray = [null]; var authorToNumMap = {}; var chatLinesByDay = []; // {day:'2009-06-17', lines: [...]} var oldestHistoricalLine = 0; var loadingMoreHistory = false; var HISTORY_LINES_TO_LOAD_AT_A_TIME = 50; function authorToNum(author, dontAddIfAbsent) { if ((typeof authorToNumMap[author]) == "number") { return authorToNumMap[author]; } else if (dontAddIfAbsent) { return -1; } else { var n = numToAuthorMap.length; numToAuthorMap.push(author); authorToNumMap[author] = n; return n; } } function getDateNumCSSDayString(dateNum) { var d = new Date(+dateNum); var year = String(d.getFullYear()); var month = ("0"+String(d.getMonth()+1)).slice(-2); var day = ("0"+String(d.getDate())).slice(-2); return year+"-"+month+"-"+day; } function getDateNumHumanDayString(dateNum) { var d = new Date(+dateNum); var monthName = (["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"])[d.getMonth()]; var dayOfMonth = d.getDate(); var year = d.getFullYear(); return monthName+" "+dayOfMonth+", "+year; } function ensureChatDay(time) { var day = getDateNumCSSDayString(time); var dayIndex = padutils.binarySearch(chatLinesByDay.length, function(n) { return chatLinesByDay[n].day >= day; }); if (dayIndex >= chatLinesByDay.length || chatLinesByDay[dayIndex].day != day) { // add new day to chat display! chatLinesByDay.splice(dayIndex, 0, {day: day, lines: []}); var dayHtml = '
'+ '

'+getDateNumHumanDayString(time)+ '

'; var dayDivs = $("#chatlines .chatday"); if (dayIndex == dayDivs.length) { $("#chatlines").append(dayHtml); } else { dayDivs.eq(dayIndex).before(dayHtml); } } return dayIndex; } function addChatLine(userId, time, name, lineText, addBefore) { var dayIndex = ensureChatDay(time); var dayDiv = $("#chatday"+getDateNumCSSDayString(time)); var d = new Date(+time); var hourmin = d.getHours()+":"+("0"+d.getMinutes()).slice(-2); var nameHtml; if (name) { nameHtml = padutils.escapeHtml(name); } else { nameHtml = "unnamed"; } var chatlineClass = "chatline"; if (userId) { var authorNum = authorToNum(userId); chatlineClass += " chatauthor"+authorNum; } var textHtml = padutils.escapeHtmlWithClickableLinks(lineText, '_blank'); var lineNode = $('
'+ ''+hourmin+' '+ ''+nameHtml+': '+ ''+textHtml+'
'); var linesArray = chatLinesByDay[dayIndex].lines; var lineObj = {userId:userId, time:time, name:name, lineText:lineText}; if (addBefore) { dayDiv.find("h2").after(lineNode); linesArray.splice(0, 0, lineObj); } else { dayDiv.append(lineNode); linesArray.push(lineObj); } if (userId) { var color = getAuthorCSSColor(userId); if (color) { lineNode.css('background', color); } } return {lineNode:lineNode}; } function receiveChatHistoryBlock(block) { for(var a in block.historicalAuthorData) { var data = block.historicalAuthorData[a]; var n = authorToNum(a); if (! authorColorArray[n]) { // no data about this author, use historical info authorColorArray[n] = { colorId: data.colorId, faded: true }; } } oldestHistoricalLine = block.start; var lines = block.lines; for(var i=lines.length-1; i>=0; i--) { var line = lines[i]; addChatLine(line.userId, line.time, line.name, line.lineText, true); } if (oldestHistoricalLine > 0) { $("a#chatloadmore").css('display', 'block'); } else { $("a#chatloadmore").css('display', 'none'); } } function fadeColor(colorCSS) { var color = colorutils.css2triple(colorCSS); color = colorutils.blend(color, [1,1,1], 0.5); return colorutils.triple2css(color); } function getAuthorCSSColor(author) { var n = authorToNum(author, true); if (n < 0) { return ''; } else { var cdata = authorColorArray[n]; if (! cdata) { return ''; } else { var c = pad.getColorPalette()[cdata.colorId]; if (cdata.faded) { c = fadeColor(c); } return c; } } } function changeAuthorColorData(author, cdata) { var n = authorToNum(author); authorColorArray[n] = cdata; var cssColor = getAuthorCSSColor(author); if (cssColor) { $("#chatlines .chatauthor"+n).css('background',cssColor); } } function sendChat() { var lineText = $("#chatentrybox").val(); if (lineText) { $("#chatentrybox").val('').focus(); var msg = { type: 'chat', userId: pad.getUserId(), lineText: lineText, senderName: pad.getUserName(), authId: pad.getUserId() }; pad.sendClientMessage(msg); self.receiveChat(msg); self.scrollToBottom(); } } var self = { init: function(chatHistoryBlock, initialUserInfo) { ensureChatDay(+new Date); // so that current date shows up right away $("a#chatloadmore").click(self.loadMoreHistory); self.handleUserJoinOrUpdate(initialUserInfo); receiveChatHistoryBlock(chatHistoryBlock); padutils.bindEnterAndEscape($("#chatentrybox"), function(evt) { // return/enter sendChat(); }, null); self.scrollToBottom(); }, receiveChat: function(msg) { var box = $("#chatlines").get(0); var wasAtBottom = (box.scrollTop - (box.scrollHeight - $(box).height()) >= -5); addChatLine(msg.userId, +new Date, msg.senderName, msg.lineText, false); if (wasAtBottom) { window.setTimeout(function() { self.scrollToBottom(); }, 0); } }, handleUserJoinOrUpdate: function(userInfo) { changeAuthorColorData(userInfo.userId, { colorId: userInfo.colorId, faded: false }); }, handleUserLeave: function(userInfo) { changeAuthorColorData(userInfo.userId, { colorId: userInfo.colorId, faded: true }); }, scrollToBottom: function() { var box = $("#chatlines").get(0); box.scrollTop = box.scrollHeight; }, scrollToTop: function() { var box = $("#chatlines").get(0); box.scrollTop = 0; }, loadMoreHistory: function() { if (loadingMoreHistory) { return; } var end = oldestHistoricalLine; var start = Math.max(0, end - HISTORY_LINES_TO_LOAD_AT_A_TIME); var padId = pad.getPadId(); loadingMoreHistory = true; $("#padchat #chatloadmore").css('display', 'none'); $("#padchat #chatloadingmore").css('display', 'block'); $.ajax({ type: 'get', url: '/ep/pad/chathistory', data: { padId: padId, start: start, end: end }, success: success, error: error }); function success(text) { notLoading(); var result = JSON.parse(text); // try to keep scrolled to the same place... var scrollBox = $("#chatlines").get(0); var scrollDeterminer = function() { return 0; }; var topLine = $("#chatlines .chatday:first .chatline:first").children().eq(0); if (topLine.length > 0) { var posTop = topLine.position().top; var scrollTop = scrollBox.scrollTop; scrollDeterminer = function() { var newPosTop = topLine.position().top; return newPosTop + (scrollTop - posTop); }; } receiveChatHistoryBlock(result); scrollBox.scrollTop = Math.max(0, Math.min(scrollBox.scrollHeight, scrollDeterminer())); } function error() { notLoading(); } function notLoading() { loadingMoreHistory = false; $("#padchat #chatloadmore").css('display', 'block'); $("#padchat #chatloadingmore").css('display', 'none'); } } }; return self; }());