kopia lustrzana https://github.com/c9/core
407 wiersze
13 KiB
JavaScript
407 wiersze
13 KiB
JavaScript
define(function(require, exports, module) {
|
|
"use strict";
|
|
|
|
main.consumes = [
|
|
"CollabPanel", "ui", "panels", "collab.util", "collab.workspace",
|
|
"collab", "Menu", "MenuItem"
|
|
];
|
|
main.provides = ["chat"];
|
|
return main;
|
|
|
|
function main(options, imports, register) {
|
|
var CollabPanel = imports.CollabPanel;
|
|
var ui = imports.ui;
|
|
var panels = imports.panels;
|
|
var util = imports["collab.util"];
|
|
var workspace = imports["collab.workspace"];
|
|
var collab = imports.collab;
|
|
var Menu = imports.Menu;
|
|
var MenuItem = imports.MenuItem;
|
|
|
|
var html = require("text!./chat.html");
|
|
var css = require("text!./chat.css");
|
|
var timeago = require("timeago");
|
|
var staticPrefix = options.staticPrefix;
|
|
|
|
var toDeleteMessage;
|
|
|
|
var ROLE_NONE = "n";
|
|
var ROLE_VISITOR = "v";
|
|
var ROLE_COLLABORATOR = "c";
|
|
var ROLE_ADMIN = "a";
|
|
|
|
var plugin = new CollabPanel("Ajax.org", main.consumes, {
|
|
name: "chat",
|
|
index: 200,
|
|
caption: "Group Chat",
|
|
textselect: true,
|
|
style: "flex:1;"
|
|
});
|
|
|
|
// var emit = plugin.getEmitter();
|
|
var emoji = require("./my_emoji");
|
|
|
|
// panel-relared UI elements
|
|
var chatInput, chatText, mnuCtxTreeEl, parent;
|
|
// non-panel related UI elements
|
|
var chatThrob, chatCounter, chatNotif;
|
|
|
|
function $(id) {
|
|
return document.getElementById(id);
|
|
}
|
|
|
|
var loaded = false;
|
|
function load() {
|
|
if (loaded) return;
|
|
loaded = true;
|
|
|
|
collab.on("chatMessage", onChatMessage);
|
|
collab.on("chatClear", onChatClear);
|
|
}
|
|
|
|
var drawn = false;
|
|
function draw(options) {
|
|
if (drawn) return;
|
|
drawn = true;
|
|
|
|
drawNonPanelElements();
|
|
|
|
parent = options.aml;
|
|
var parentExt = parent.$int;
|
|
parentExt.className += " chatContainer";
|
|
|
|
chatText = parentExt.appendChild(document.createElement("div"));
|
|
chatText.setAttribute("class", "chatText");
|
|
|
|
chatInput = new apf.codebox({
|
|
htmlNode: parentExt,
|
|
skin: "codebox",
|
|
"initial-message": "Enter your message here",
|
|
// clearbutton : "true",
|
|
focusselect: "true"
|
|
});
|
|
|
|
chatInput.$ext.classList.remove("ace_searchbox");
|
|
chatInput.ace.setOptions({
|
|
wrap: "free",
|
|
indentedSoftWrap: false,
|
|
maxLines: 5,
|
|
minLines: 2,
|
|
fontFamily: "inherit"
|
|
});
|
|
chatInput.ace.session.$setFontMetrics(chatInput.ace.renderer.$fontMetrics);
|
|
|
|
plugin.addElement(chatInput);
|
|
|
|
function onWorkspaceSync() {
|
|
var accessInfo = workspace.accessInfo;
|
|
if (accessInfo.private && (!accessInfo.member || accessInfo.pending))
|
|
return console.warn("Don't have read access - You can't use chat");
|
|
var chatHistory = workspace.chatHistory || [];
|
|
chatHistory.forEach(addMessage);
|
|
scrollDown();
|
|
chatCounter.textContent = chatHistory.length;
|
|
}
|
|
|
|
chatInput.ace.commands.addCommands([
|
|
{
|
|
bindKey: "ESC",
|
|
exec: function() {
|
|
if (chatInput.getValue())
|
|
chatInput.setValue("");
|
|
else
|
|
collab.hide();
|
|
}
|
|
}, {
|
|
bindKey: "Enter",
|
|
exec: send
|
|
}
|
|
]);
|
|
|
|
function listenWorkspaceSync() {
|
|
workspace.off("sync", onWorkspaceSync);
|
|
workspace.on("sync", onWorkspaceSync);
|
|
}
|
|
|
|
if (panels.isActive("collab"))
|
|
listenWorkspaceSync();
|
|
|
|
collab.on("show", function() {
|
|
chatInput.focus();
|
|
listenWorkspaceSync();
|
|
});
|
|
|
|
collab.on("hide", function() {
|
|
workspace.off("sync", onWorkspaceSync);
|
|
});
|
|
|
|
var deleteMsgItem = new MenuItem({
|
|
caption: "Delete Message",
|
|
match: "rw",
|
|
onclick: clearChatMessage,
|
|
disabled: true
|
|
});
|
|
|
|
var clearHistoryItem = new MenuItem({
|
|
caption: "Clear history",
|
|
match: "rw",
|
|
onclick: clearChatHistory,
|
|
disabled: true
|
|
});
|
|
|
|
var mnuCtxTree = new Menu({
|
|
id: "mnuChat",
|
|
items: [
|
|
deleteMsgItem,
|
|
clearHistoryItem
|
|
]
|
|
}, plugin);
|
|
|
|
mnuCtxTreeEl = mnuCtxTree.aml;
|
|
|
|
parent.setAttribute("contextmenu", mnuCtxTreeEl);
|
|
|
|
mnuCtxTree.on("show", function() {
|
|
setTimeout(function() {
|
|
var hasReadWrite = workspace.fs === "rw";
|
|
var collabConnected = collab.connected;
|
|
clearHistoryItem.disabled = !hasReadWrite || !collabConnected || !chatText.firstChild;
|
|
toDeleteMessage = findMessageToDelete(hasReadWrite);
|
|
deleteMsgItem.disabled = !toDeleteMessage || !collabConnected;
|
|
}, 10);
|
|
});
|
|
|
|
parent.on("resize", function() {
|
|
if (isOpen()) {
|
|
pending.forEach(addMessage);
|
|
pending = [];
|
|
updateCaption();
|
|
}
|
|
});
|
|
}
|
|
|
|
function findMessageToDelete(hasReadWrite) {
|
|
var menuRect = mnuCtxTreeEl.$int.getBoundingClientRect();
|
|
var msg;
|
|
[].slice.apply(chatText.getElementsByTagName("p")).forEach(function(msgP) {
|
|
var rect = msgP.getBoundingClientRect();
|
|
if (rect.left < menuRect.left && (rect.left + rect.width) > menuRect.left
|
|
&& rect.top < menuRect.top && (rect.top + rect.height) > menuRect.top
|
|
&& (hasReadWrite || msgP.userId == workspace.myUserId))
|
|
msg = msgP;
|
|
});
|
|
return msg;
|
|
}
|
|
|
|
function clearChatMessage() {
|
|
if (!toDeleteMessage || !toDeleteMessage.id)
|
|
return console.error("[OT] Chat: no message found to delete!");
|
|
var msgId = toDeleteMessage.id.match(/ot_chat_(\d+)/)[1];
|
|
collab.send("CLEAR_CHAT", { id: msgId });
|
|
}
|
|
|
|
function clearChatHistory() {
|
|
collab.send("CLEAR_CHAT", { clear: true });
|
|
}
|
|
|
|
var seenMsgs = {};
|
|
var pending = [];
|
|
var throbTimeout;
|
|
|
|
function scrollDown() {
|
|
chatText.scrollTop = chatText.scrollHeight;
|
|
}
|
|
|
|
function isOpen() {
|
|
return panels.isActive("collab") && parent.state[0] != "m";
|
|
}
|
|
|
|
function send() {
|
|
var text = chatInput.getValue().trim();
|
|
if (!text)
|
|
return;
|
|
text = emoji.toEmojiUnicode(text);
|
|
collab.send("CHAT_MESSAGE", { text: text });
|
|
chatInput.setValue("");
|
|
}
|
|
|
|
function getAuthorName(userId) {
|
|
if (userId == workspace.myUserId)
|
|
return "You";
|
|
|
|
var user = workspace.users[userId];
|
|
return util.escapeHTML(user.fullname);
|
|
}
|
|
|
|
function getAuthorColor(userId) {
|
|
var color = workspace.colorPool[userId];
|
|
return util.formatColor(color);
|
|
}
|
|
|
|
function formatMessageText(text) {
|
|
text = util.escapeHtmlWithClickableLinks(text.trim(), "_blank");
|
|
text = text.replace(/\n/g, "<br/>");
|
|
text = emoji.emoji(text, staticPrefix);
|
|
return text;
|
|
}
|
|
|
|
function onChatMessage(msg) {
|
|
drawNonPanelElements();
|
|
workspace.addChatMessage(msg);
|
|
if (isOpen()) {
|
|
addMessage(msg);
|
|
}
|
|
else {
|
|
pending.push(msg);
|
|
updateCaption();
|
|
|
|
var throbText = "<b>" + getAuthorName(msg.userId) + "</b> ";
|
|
var text = formatMessageText(msg.text);
|
|
var notif = msg.notification;
|
|
if (notif) {
|
|
throbText += text + " " + notif.linkText;
|
|
} else {
|
|
throbText += ": " + text;
|
|
}
|
|
|
|
chatThrob.innerHTML = throbText;
|
|
chatThrob.style.display = "block";
|
|
clearTimeout(throbTimeout);
|
|
throbTimeout = setTimeout(function () {
|
|
chatThrob.style.display = "none";
|
|
}, 5000);
|
|
}
|
|
|
|
if (msg.increment) {
|
|
var count = Number(chatCounter.textContent);
|
|
chatCounter.textContent = count + 1;
|
|
}
|
|
|
|
var inputFocussed = chatInput && chatInput.ace.isFocused();
|
|
if (!inputFocussed)
|
|
chatNotif.play();
|
|
|
|
}
|
|
|
|
function onChatClear(data) {
|
|
if (data.clear) {
|
|
// fast way to delete all children - not innerHtml = ''
|
|
while (chatText.firstChild)
|
|
chatText.removeChild(chatText.firstChild);
|
|
seenMsgs = {}; // sql truncate table would lead to same msg ids
|
|
workspace.chatHistory = [];
|
|
}
|
|
else if (data.id) {
|
|
var msgHtml = $("ot_chat_" + data.id);
|
|
msgHtml && msgHtml.parentNode.removeChild(msgHtml);
|
|
}
|
|
}
|
|
|
|
function addMessage(msg) {
|
|
if (seenMsgs[msg.id])
|
|
return;
|
|
seenMsgs[msg.id] = true;
|
|
// correct the time
|
|
// msg.timestamp += clientTimeOffset;
|
|
var msgDate = new Date(msg.timestamp);
|
|
|
|
// create the time string
|
|
var text = formatMessageText(msg.text);
|
|
var authorName = getAuthorName(msg.userId);
|
|
var authorColor = getAuthorColor(msg.userId);
|
|
|
|
var authorNameEl = document.createElement("a");
|
|
authorNameEl.href = "javascript:void(0)";
|
|
authorNameEl.className = "authorName";
|
|
authorNameEl.innerHTML = "<b>" + authorName + "</b>";
|
|
// authorNameEl.addEventListener("click", function () {
|
|
// });
|
|
|
|
var html = document.createElement("p");
|
|
html.id = "ot_chat_" + msg.id;
|
|
html.userId = msg.userId;
|
|
|
|
if (msg.userId == workspace.myUserId)
|
|
html.className = "you";
|
|
|
|
var borderEl = document.createElement("span");
|
|
html.appendChild(borderEl);
|
|
borderEl.className = "chatBorder";
|
|
borderEl.style.borderLeftColor = authorColor;
|
|
|
|
html.appendChild(authorNameEl);
|
|
var textEl = document.createElement("span");
|
|
textEl.className = "chatmessage";
|
|
textEl.innerHTML = text + "<br/>";
|
|
html.appendChild(textEl);
|
|
var timeEl = document.createElement("span");
|
|
timeEl.className = "chattime";
|
|
timeEl.title = msgDate.toISOString();
|
|
timeEl.innerHTML = msgDate;
|
|
timeago(timeEl);
|
|
html.appendChild(timeEl);
|
|
|
|
chatText.appendChild(html);
|
|
scrollDown();
|
|
}
|
|
|
|
function updateCaption() {
|
|
var caption = "Group Chat";
|
|
if (pending.length)
|
|
caption += "(" + pending.length + ")";
|
|
if (parent)
|
|
parent.setAttribute("caption", caption);
|
|
}
|
|
|
|
var nonPanelDrawn = false;
|
|
function drawNonPanelElements () {
|
|
if (nonPanelDrawn) return;
|
|
nonPanelDrawn = true;
|
|
|
|
ui.insertHtml(null, html, plugin);
|
|
ui.insertCss(css, staticPrefix, plugin);
|
|
|
|
chatThrob = $("chatThrob");
|
|
chatCounter = $("chatCounter");
|
|
chatNotif = $("chatNotif");
|
|
chatNotif.src = staticPrefix + "/sounds/chat_notif.mp3";
|
|
|
|
chatThrob.addEventListener("click", function () {
|
|
chatThrob.style.display = "none";
|
|
plugin.show();
|
|
});
|
|
}
|
|
|
|
/***** Lifecycle *****/
|
|
plugin.on("load", function() {
|
|
load();
|
|
plugin.once("draw", draw);
|
|
});
|
|
plugin.on("enable", function() {
|
|
|
|
});
|
|
plugin.on("disable", function() {
|
|
});
|
|
|
|
plugin.on("unload", function() {
|
|
loaded = false;
|
|
drawn = nonPanelDrawn = false;
|
|
});
|
|
|
|
/***** Register and define API *****/
|
|
|
|
/**
|
|
* Adds File->New File and File->New Folder menu items as well as the
|
|
* commands for opening a new file as well as an API.
|
|
* @singleton
|
|
**/
|
|
plugin.freezePublicAPI({
|
|
});
|
|
|
|
register(null, {
|
|
chat: plugin
|
|
});
|
|
}
|
|
|
|
});
|