pad.pub0.org/static/js/pad_chat.js

295 lines
8.9 KiB
JavaScript

/**
* 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 = '<div class="chatday" id="chatday'+day+'">'+
'<h2 class="dayheader">'+getDateNumHumanDayString(time)+
'</h2></div>';
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 = "<i>unnamed</i>";
}
var chatlineClass = "chatline";
if (userId) {
var authorNum = authorToNum(userId);
chatlineClass += " chatauthor"+authorNum;
}
var textHtml = padutils.escapeHtmlWithClickableLinks(lineText, '_blank');
var lineNode = $('<div class="'+chatlineClass+'">'+
'<span class="chatlinetime">'+hourmin+' </span>'+
'<span class="chatlinename">'+nameHtml+': </span>'+
'<span class="chatlinetext">'+textHtml+'</span></div>');
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;
}());