mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-05-09 08:25:00 -04:00
Merge fa3020dffd
into dae0cbfb53
This commit is contained in:
commit
1f596c6d77
9 changed files with 399 additions and 197 deletions
|
@ -159,11 +159,7 @@ exports.handleDisconnect = function(client)
|
||||||
*/
|
*/
|
||||||
exports.handleMessage = function(client, message)
|
exports.handleMessage = function(client, message)
|
||||||
{
|
{
|
||||||
_.map(hooks.callAll( "handleMessage", { client: client, message: message }), function ( newmessage ) {
|
|
||||||
if ( newmessage || newmessage === null ) {
|
|
||||||
message = newmessage;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if(message == null)
|
if(message == null)
|
||||||
{
|
{
|
||||||
messageLogger.warn("Message is null!");
|
messageLogger.warn("Message is null!");
|
||||||
|
@ -175,6 +171,23 @@ exports.handleMessage = function(client, message)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var handleMessageHook = function(callback){
|
||||||
|
var dropMessage = false;
|
||||||
|
|
||||||
|
// Call handleMessage hook. If a plugin returns null, the message will be dropped. Note that for all messages
|
||||||
|
// handleMessage will be called, even if the client is not authorized
|
||||||
|
hooks.aCallAll("handleMessage", { client: client, message: message }, function ( messages ) {
|
||||||
|
_.each(messages, function(newMessage){
|
||||||
|
if ( newmessage === null ) {
|
||||||
|
dropMessage = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// If no plugins explicitly told us to drop the message, its ok to proceed
|
||||||
|
if(!dropMessage){ callback() };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
var finalHandler = function () {
|
var finalHandler = function () {
|
||||||
//Check what type of message we get and delegate to the other methodes
|
//Check what type of message we get and delegate to the other methodes
|
||||||
if(message.type == "CLIENT_READY") {
|
if(message.type == "CLIENT_READY") {
|
||||||
|
@ -203,11 +216,18 @@ exports.handleMessage = function(client, message)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (message && message.padId) {
|
if (message) {
|
||||||
async.series([
|
async.series([
|
||||||
|
handleMessageHook,
|
||||||
//check permissions
|
//check permissions
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
if(!message.padId){
|
||||||
|
// If the message has a padId we assume the client is already known to the server and needs no re-authorization
|
||||||
|
callback();
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Note: message.sessionID is an entirely different kind of
|
// Note: message.sessionID is an entirely different kind of
|
||||||
// session from the sessions we use here! Beware! FIXME: Call
|
// session from the sessions we use here! Beware! FIXME: Call
|
||||||
// our "sessions" "connections".
|
// our "sessions" "connections".
|
||||||
|
@ -231,8 +251,6 @@ exports.handleMessage = function(client, message)
|
||||||
},
|
},
|
||||||
finalHandler
|
finalHandler
|
||||||
]);
|
]);
|
||||||
} else {
|
|
||||||
finalHandler();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ var os = require("os");
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var argv = require('./Cli').argv;
|
var argv = require('./Cli').argv;
|
||||||
var npm = require("npm/lib/npm.js");
|
var npm = require("npm/lib/npm.js");
|
||||||
|
var vm = require('vm');
|
||||||
|
|
||||||
/* Root path of the installation */
|
/* Root path of the installation */
|
||||||
exports.root = path.normalize(path.join(npm.dir, ".."));
|
exports.root = path.normalize(path.join(npm.dir, ".."));
|
||||||
|
@ -45,6 +46,7 @@ exports.dbType = "dirty";
|
||||||
* This setting is passed with dbType to ueberDB to set up the database
|
* This setting is passed with dbType to ueberDB to set up the database
|
||||||
*/
|
*/
|
||||||
exports.dbSettings = { "filename" : path.join(exports.root, "dirty.db") };
|
exports.dbSettings = { "filename" : path.join(exports.root, "dirty.db") };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default Text of a new pad
|
* The default Text of a new pad
|
||||||
*/
|
*/
|
||||||
|
@ -102,32 +104,24 @@ exports.abiwordAvailable = function()
|
||||||
|
|
||||||
// Discover where the settings file lives
|
// Discover where the settings file lives
|
||||||
var settingsFilename = argv.settings || "settings.json";
|
var settingsFilename = argv.settings || "settings.json";
|
||||||
if (settingsFilename.charAt(0) != '/') {
|
settingsFilename = path.resolve(path.join(root, settingsFilename));
|
||||||
settingsFilename = path.normalize(path.join(root, settingsFilename));
|
|
||||||
}
|
|
||||||
|
|
||||||
var settingsStr
|
var settingsStr;
|
||||||
try{
|
try{
|
||||||
//read the settings sync
|
//read the settings sync
|
||||||
settingsStr = fs.readFileSync(settingsFilename).toString();
|
settingsStr = fs.readFileSync(settingsFilename).toString();
|
||||||
} catch(e){
|
} catch(e){
|
||||||
console.warn('No settings file found. Using defaults.');
|
console.warn('No settings file found. Continuing using defaults!');
|
||||||
settingsStr = '{}';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//remove all comments
|
|
||||||
settingsStr = settingsStr.replace(/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/gm,"").replace(/#.*/g,"").replace(/\/\/.*/g,"");
|
|
||||||
|
|
||||||
//try to parse the settings
|
// try to parse the settings
|
||||||
var settings;
|
var settings;
|
||||||
try
|
try {
|
||||||
{
|
if(settingsStr) {
|
||||||
settings = JSON.parse(settingsStr);
|
settings = vm.runInContext('exports = '+settingsStr, vm.createContext(), "settings.json");
|
||||||
}
|
}
|
||||||
catch(e)
|
}catch(e){
|
||||||
{
|
console.error('There was an error processing your settings.json file: '+e.message);
|
||||||
console.error("There is a syntax error in your settings.json file");
|
|
||||||
console.error(e.message);
|
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,8 +142,7 @@ for(var i in settings)
|
||||||
//this setting is unkown, output a warning and throw it away
|
//this setting is unkown, output a warning and throw it away
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
console.warn("Unkown Setting: '" + i + "'");
|
console.warn("Unknown Setting: '" + i + "'. This setting doesn't exist or it was removed");
|
||||||
console.warn("This setting doesn't exist or it was removed");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,8 +27,6 @@
|
||||||
|
|
||||||
var AttributePool = require("./AttributePool");
|
var AttributePool = require("./AttributePool");
|
||||||
|
|
||||||
var _opt = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ==================== General Util Functions =======================
|
* ==================== General Util Functions =======================
|
||||||
*/
|
*/
|
||||||
|
@ -127,22 +125,13 @@ exports.opIterator = function (opsStr, optStartIndex) {
|
||||||
function nextRegexMatch() {
|
function nextRegexMatch() {
|
||||||
prevIndex = curIndex;
|
prevIndex = curIndex;
|
||||||
var result;
|
var result;
|
||||||
if (_opt) {
|
regex.lastIndex = curIndex;
|
||||||
result = _opt.nextOpInString(opsStr, curIndex);
|
result = regex.exec(opsStr);
|
||||||
if (result) {
|
curIndex = regex.lastIndex;
|
||||||
if (result.opcode() == '?') {
|
if (result[0] == '?') {
|
||||||
exports.error("Hit error opcode in op stream");
|
exports.error("Hit error opcode in op stream");
|
||||||
}
|
|
||||||
curIndex = result.lastIndex();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
regex.lastIndex = curIndex;
|
|
||||||
result = regex.exec(opsStr);
|
|
||||||
curIndex = regex.lastIndex;
|
|
||||||
if (result[0] == '?') {
|
|
||||||
exports.error("Hit error opcode in op stream");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
var regexResult = nextRegexMatch();
|
var regexResult = nextRegexMatch();
|
||||||
|
@ -150,13 +139,7 @@ exports.opIterator = function (opsStr, optStartIndex) {
|
||||||
|
|
||||||
function next(optObj) {
|
function next(optObj) {
|
||||||
var op = (optObj || obj);
|
var op = (optObj || obj);
|
||||||
if (_opt && regexResult) {
|
if (regexResult[0]) {
|
||||||
op.attribs = regexResult.attribs();
|
|
||||||
op.lines = regexResult.lines();
|
|
||||||
op.chars = regexResult.chars();
|
|
||||||
op.opcode = regexResult.opcode();
|
|
||||||
regexResult = nextRegexMatch();
|
|
||||||
} else if ((!_opt) && regexResult[0]) {
|
|
||||||
op.attribs = regexResult[1];
|
op.attribs = regexResult[1];
|
||||||
op.lines = exports.parseNum(regexResult[2] || 0);
|
op.lines = exports.parseNum(regexResult[2] || 0);
|
||||||
op.opcode = regexResult[3];
|
op.opcode = regexResult[3];
|
||||||
|
@ -169,7 +152,7 @@ exports.opIterator = function (opsStr, optStartIndex) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasNext() {
|
function hasNext() {
|
||||||
return !!(_opt ? regexResult : regexResult[0]);
|
return !!(regexResult[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function lastIndex() {
|
function lastIndex() {
|
||||||
|
@ -414,159 +397,109 @@ exports.smartOpAssembler = function () {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
if (_opt) {
|
|
||||||
exports.mergingOpAssembler = function () {
|
|
||||||
var assem = _opt.mergingOpAssembler();
|
|
||||||
|
|
||||||
function append(op) {
|
exports.mergingOpAssembler = function () {
|
||||||
assem.append(op.opcode, op.chars, op.lines, op.attribs);
|
// This assembler can be used in production; it efficiently
|
||||||
}
|
// merges consecutive operations that are mergeable, ignores
|
||||||
|
// no-ops, and drops final pure "keeps". It does not re-order
|
||||||
|
// operations.
|
||||||
|
var assem = exports.opAssembler();
|
||||||
|
var bufOp = exports.newOp();
|
||||||
|
|
||||||
function toString() {
|
// If we get, for example, insertions [xxx\n,yyy], those don't merge,
|
||||||
return assem.toString();
|
// but if we get [xxx\n,yyy,zzz\n], that merges to [xxx\nyyyzzz\n].
|
||||||
}
|
// This variable stores the length of yyy and any other newline-less
|
||||||
|
// ops immediately after it.
|
||||||
|
var bufOpAdditionalCharsAfterNewline = 0;
|
||||||
|
|
||||||
function clear() {
|
function flush(isEndDocument) {
|
||||||
assem.clear();
|
if (bufOp.opcode) {
|
||||||
}
|
if (isEndDocument && bufOp.opcode == '=' && !bufOp.attribs) {
|
||||||
|
// final merged keep, leave it implicit
|
||||||
function endDocument() {
|
} else {
|
||||||
assem.endDocument();
|
assem.append(bufOp);
|
||||||
}
|
if (bufOpAdditionalCharsAfterNewline) {
|
||||||
|
bufOp.chars = bufOpAdditionalCharsAfterNewline;
|
||||||
return {
|
bufOp.lines = 0;
|
||||||
append: append,
|
|
||||||
toString: toString,
|
|
||||||
clear: clear,
|
|
||||||
endDocument: endDocument
|
|
||||||
};
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
exports.mergingOpAssembler = function () {
|
|
||||||
// This assembler can be used in production; it efficiently
|
|
||||||
// merges consecutive operations that are mergeable, ignores
|
|
||||||
// no-ops, and drops final pure "keeps". It does not re-order
|
|
||||||
// operations.
|
|
||||||
var assem = exports.opAssembler();
|
|
||||||
var bufOp = exports.newOp();
|
|
||||||
|
|
||||||
// If we get, for example, insertions [xxx\n,yyy], those don't merge,
|
|
||||||
// but if we get [xxx\n,yyy,zzz\n], that merges to [xxx\nyyyzzz\n].
|
|
||||||
// This variable stores the length of yyy and any other newline-less
|
|
||||||
// ops immediately after it.
|
|
||||||
var bufOpAdditionalCharsAfterNewline = 0;
|
|
||||||
|
|
||||||
function flush(isEndDocument) {
|
|
||||||
if (bufOp.opcode) {
|
|
||||||
if (isEndDocument && bufOp.opcode == '=' && !bufOp.attribs) {
|
|
||||||
// final merged keep, leave it implicit
|
|
||||||
} else {
|
|
||||||
assem.append(bufOp);
|
assem.append(bufOp);
|
||||||
if (bufOpAdditionalCharsAfterNewline) {
|
bufOpAdditionalCharsAfterNewline = 0;
|
||||||
bufOp.chars = bufOpAdditionalCharsAfterNewline;
|
|
||||||
bufOp.lines = 0;
|
|
||||||
assem.append(bufOp);
|
|
||||||
bufOpAdditionalCharsAfterNewline = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
bufOp.opcode = '';
|
|
||||||
}
|
}
|
||||||
|
bufOp.opcode = '';
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function append(op) {
|
function append(op) {
|
||||||
if (op.chars > 0) {
|
if (op.chars > 0) {
|
||||||
if (bufOp.opcode == op.opcode && bufOp.attribs == op.attribs) {
|
if (bufOp.opcode == op.opcode && bufOp.attribs == op.attribs) {
|
||||||
if (op.lines > 0) {
|
if (op.lines > 0) {
|
||||||
// bufOp and additional chars are all mergeable into a multi-line op
|
// bufOp and additional chars are all mergeable into a multi-line op
|
||||||
bufOp.chars += bufOpAdditionalCharsAfterNewline + op.chars;
|
bufOp.chars += bufOpAdditionalCharsAfterNewline + op.chars;
|
||||||
bufOp.lines += op.lines;
|
bufOp.lines += op.lines;
|
||||||
bufOpAdditionalCharsAfterNewline = 0;
|
bufOpAdditionalCharsAfterNewline = 0;
|
||||||
} else if (bufOp.lines == 0) {
|
} else if (bufOp.lines == 0) {
|
||||||
// both bufOp and op are in-line
|
// both bufOp and op are in-line
|
||||||
bufOp.chars += op.chars;
|
bufOp.chars += op.chars;
|
||||||
} else {
|
|
||||||
// append in-line text to multi-line bufOp
|
|
||||||
bufOpAdditionalCharsAfterNewline += op.chars;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
flush();
|
// append in-line text to multi-line bufOp
|
||||||
exports.copyOp(op, bufOp);
|
bufOpAdditionalCharsAfterNewline += op.chars;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
flush();
|
||||||
|
exports.copyOp(op, bufOp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function endDocument() {
|
function endDocument() {
|
||||||
flush(true);
|
flush(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
function toString() {
|
function toString() {
|
||||||
flush();
|
flush();
|
||||||
return assem.toString();
|
return assem.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
function clear() {
|
function clear() {
|
||||||
assem.clear();
|
assem.clear();
|
||||||
exports.clearOp(bufOp);
|
exports.clearOp(bufOp);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
append: append,
|
append: append,
|
||||||
toString: toString,
|
toString: toString,
|
||||||
clear: clear,
|
clear: clear,
|
||||||
endDocument: endDocument
|
endDocument: endDocument
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
if (_opt) {
|
|
||||||
exports.opAssembler = function () {
|
|
||||||
var assem = _opt.opAssembler();
|
|
||||||
// this function allows op to be mutated later (doesn't keep a ref)
|
|
||||||
|
|
||||||
function append(op) {
|
|
||||||
assem.append(op.opcode, op.chars, op.lines, op.attribs);
|
exports.opAssembler = function () {
|
||||||
|
var pieces = [];
|
||||||
|
// this function allows op to be mutated later (doesn't keep a ref)
|
||||||
|
|
||||||
|
function append(op) {
|
||||||
|
pieces.push(op.attribs);
|
||||||
|
if (op.lines) {
|
||||||
|
pieces.push('|', exports.numToString(op.lines));
|
||||||
}
|
}
|
||||||
|
pieces.push(op.opcode);
|
||||||
|
pieces.push(exports.numToString(op.chars));
|
||||||
|
}
|
||||||
|
|
||||||
function toString() {
|
function toString() {
|
||||||
return assem.toString();
|
return pieces.join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
function clear() {
|
function clear() {
|
||||||
assem.clear();
|
pieces.length = 0;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
append: append,
|
append: append,
|
||||||
toString: toString,
|
toString: toString,
|
||||||
clear: clear
|
clear: clear
|
||||||
};
|
|
||||||
};
|
};
|
||||||
} else {
|
};
|
||||||
exports.opAssembler = function () {
|
|
||||||
var pieces = [];
|
|
||||||
// this function allows op to be mutated later (doesn't keep a ref)
|
|
||||||
|
|
||||||
function append(op) {
|
|
||||||
pieces.push(op.attribs);
|
|
||||||
if (op.lines) {
|
|
||||||
pieces.push('|', exports.numToString(op.lines));
|
|
||||||
}
|
|
||||||
pieces.push(op.opcode);
|
|
||||||
pieces.push(exports.numToString(op.chars));
|
|
||||||
}
|
|
||||||
|
|
||||||
function toString() {
|
|
||||||
return pieces.join('');
|
|
||||||
}
|
|
||||||
|
|
||||||
function clear() {
|
|
||||||
pieces.length = 0;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
append: append,
|
|
||||||
toString: toString,
|
|
||||||
clear: clear
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A custom made String Iterator
|
* A custom made String Iterator
|
||||||
|
|
|
@ -23,11 +23,12 @@
|
||||||
var padutils = require('./pad_utils').padutils;
|
var padutils = require('./pad_utils').padutils;
|
||||||
var padcookie = require('./pad_cookie').padcookie;
|
var padcookie = require('./pad_cookie').padcookie;
|
||||||
|
|
||||||
|
require('./tinycon');
|
||||||
|
|
||||||
var chat = (function()
|
var chat = (function()
|
||||||
{
|
{
|
||||||
var isStuck = false;
|
var isStuck = false;
|
||||||
var chatMentions = 0;
|
var chatMentions = 0;
|
||||||
var title = document.title;
|
|
||||||
var self = {
|
var self = {
|
||||||
show: function ()
|
show: function ()
|
||||||
{
|
{
|
||||||
|
@ -35,7 +36,7 @@ var chat = (function()
|
||||||
$("#chatbox").show();
|
$("#chatbox").show();
|
||||||
self.scrollDown();
|
self.scrollDown();
|
||||||
chatMentions = 0;
|
chatMentions = 0;
|
||||||
document.title = title;
|
Tinycon.setBubble(0);
|
||||||
},
|
},
|
||||||
stickToScreen: function(fromInitialCall) // Make chat stick to right hand side of screen
|
stickToScreen: function(fromInitialCall) // Make chat stick to right hand side of screen
|
||||||
{
|
{
|
||||||
|
@ -62,8 +63,12 @@ var chat = (function()
|
||||||
},
|
},
|
||||||
scrollDown: function()
|
scrollDown: function()
|
||||||
{
|
{
|
||||||
if($('#chatbox').css("display") != "none")
|
if($('#chatbox').css("display") != "none"){
|
||||||
$('#chattext').animate({scrollTop: $('#chattext')[0].scrollHeight}, "slow");
|
if(!self.lastMessage || !self.lastMessage.position() || self.lastMessage.position().top < $('#chattext').height()) {
|
||||||
|
$('#chattext').animate({scrollTop: $('#chattext')[0].scrollHeight}, "slow");
|
||||||
|
self.lastMessage = $('#chattext > p').eq(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
send: function()
|
send: function()
|
||||||
{
|
{
|
||||||
|
@ -122,12 +127,9 @@ var chat = (function()
|
||||||
// chat throb stuff -- Just make it throw for twice as long
|
// chat throb stuff -- Just make it throw for twice as long
|
||||||
if(wasMentioned && !alreadyFocused)
|
if(wasMentioned && !alreadyFocused)
|
||||||
{ // If the user was mentioned show for twice as long and flash the browser window
|
{ // If the user was mentioned show for twice as long and flash the browser window
|
||||||
if (chatMentions == 0){
|
|
||||||
title = document.title;
|
|
||||||
}
|
|
||||||
$('#chatthrob').html("<b>"+authorName+"</b>" + ": " + text).show().delay(4000).hide(400);
|
$('#chatthrob').html("<b>"+authorName+"</b>" + ": " + text).show().delay(4000).hide(400);
|
||||||
chatMentions++;
|
chatMentions++;
|
||||||
document.title = "("+chatMentions+") " + title;
|
Tinycon.setBubble(chatMentions);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -137,7 +139,7 @@ var chat = (function()
|
||||||
// Clear the chat mentions when the user clicks on the chat input box
|
// Clear the chat mentions when the user clicks on the chat input box
|
||||||
$('#chatinput').click(function(){
|
$('#chatinput').click(function(){
|
||||||
chatMentions = 0;
|
chatMentions = 0;
|
||||||
document.title = title;
|
Tinycon.setBubble(0);
|
||||||
});
|
});
|
||||||
self.scrollDown();
|
self.scrollDown();
|
||||||
|
|
||||||
|
|
|
@ -790,8 +790,6 @@ var pad = {
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
padsavedrevs.handleIsFullyConnected(isConnected);
|
|
||||||
|
|
||||||
// pad.determineSidebarVisibility(isConnected && !isInitialConnect);
|
// pad.determineSidebarVisibility(isConnected && !isInitialConnect);
|
||||||
pad.determineChatVisibility(isConnected && !isInitialConnect);
|
pad.determineChatVisibility(isConnected && !isInitialConnect);
|
||||||
pad.determineAuthorshipColorsVisibility();
|
pad.determineAuthorshipColorsVisibility();
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var padutils = require('./pad_utils').padutils;
|
var padutils = require('./pad_utils').padutils;
|
||||||
|
var hooks = require('./pluginfw/hooks');
|
||||||
|
|
||||||
var myUserInfo = {};
|
var myUserInfo = {};
|
||||||
|
|
||||||
|
@ -529,6 +530,10 @@ var paduserlist = (function()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hooks.callAll('userJoinOrUpdate', {
|
||||||
|
userInfo: info
|
||||||
|
});
|
||||||
|
|
||||||
var userData = {};
|
var userData = {};
|
||||||
userData.color = typeof info.colorId == "number" ? clientVars.colorPalette[info.colorId] : info.colorId;
|
userData.color = typeof info.colorId == "number" ? clientVars.colorPalette[info.colorId] : info.colorId;
|
||||||
userData.name = info.name;
|
userData.name = info.name;
|
||||||
|
|
237
src/static/js/tinycon.js
Normal file
237
src/static/js/tinycon.js
Normal file
|
@ -0,0 +1,237 @@
|
||||||
|
/*!
|
||||||
|
* Tinycon - A small library for manipulating the Favicon
|
||||||
|
* Tom Moor, http://tommoor.com
|
||||||
|
* Copyright (c) 2012 Tom Moor
|
||||||
|
* MIT Licensed
|
||||||
|
* @version 0.2.6
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function(){
|
||||||
|
|
||||||
|
var Tinycon = {};
|
||||||
|
var currentFavicon = null;
|
||||||
|
var originalFavicon = null;
|
||||||
|
var originalTitle = document.title;
|
||||||
|
var faviconImage = null;
|
||||||
|
var canvas = null;
|
||||||
|
var options = {};
|
||||||
|
var defaults = {
|
||||||
|
width: 7,
|
||||||
|
height: 9,
|
||||||
|
font: '10px arial',
|
||||||
|
colour: '#ffffff',
|
||||||
|
background: '#F03D25',
|
||||||
|
fallback: true
|
||||||
|
};
|
||||||
|
|
||||||
|
var ua = (function () {
|
||||||
|
var agent = navigator.userAgent.toLowerCase();
|
||||||
|
// New function has access to 'agent' via closure
|
||||||
|
return function (browser) {
|
||||||
|
return agent.indexOf(browser) !== -1;
|
||||||
|
};
|
||||||
|
}());
|
||||||
|
|
||||||
|
var browser = {
|
||||||
|
ie: ua('msie'),
|
||||||
|
chrome: ua('chrome'),
|
||||||
|
webkit: ua('chrome') || ua('safari'),
|
||||||
|
safari: ua('safari') && !ua('chrome'),
|
||||||
|
mozilla: ua('mozilla') && !ua('chrome') && !ua('safari')
|
||||||
|
};
|
||||||
|
|
||||||
|
// private methods
|
||||||
|
var getFaviconTag = function(){
|
||||||
|
|
||||||
|
var links = document.getElementsByTagName('link');
|
||||||
|
|
||||||
|
for(var i=0, len=links.length; i < len; i++) {
|
||||||
|
if ((links[i].getAttribute('rel') || '').match(/\bicon\b/)) {
|
||||||
|
return links[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
var removeFaviconTag = function(){
|
||||||
|
|
||||||
|
var links = document.getElementsByTagName('link');
|
||||||
|
var head = document.getElementsByTagName('head')[0];
|
||||||
|
|
||||||
|
for(var i=0, len=links.length; i < len; i++) {
|
||||||
|
var exists = (typeof(links[i]) !== 'undefined');
|
||||||
|
if (exists && links[i].getAttribute('rel') === 'icon') {
|
||||||
|
head.removeChild(links[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var getCurrentFavicon = function(){
|
||||||
|
|
||||||
|
if (!originalFavicon || !currentFavicon) {
|
||||||
|
var tag = getFaviconTag();
|
||||||
|
originalFavicon = currentFavicon = tag ? tag.getAttribute('href') : '/favicon.ico';
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentFavicon;
|
||||||
|
};
|
||||||
|
|
||||||
|
var getCanvas = function (){
|
||||||
|
|
||||||
|
if (!canvas) {
|
||||||
|
canvas = document.createElement("canvas");
|
||||||
|
canvas.width = 16;
|
||||||
|
canvas.height = 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
return canvas;
|
||||||
|
};
|
||||||
|
|
||||||
|
var setFaviconTag = function(url){
|
||||||
|
removeFaviconTag();
|
||||||
|
|
||||||
|
var link = document.createElement('link');
|
||||||
|
link.type = 'image/x-icon';
|
||||||
|
link.rel = 'icon';
|
||||||
|
link.href = url;
|
||||||
|
document.getElementsByTagName('head')[0].appendChild(link);
|
||||||
|
};
|
||||||
|
|
||||||
|
var log = function(message){
|
||||||
|
if (window.console) window.console.log(message);
|
||||||
|
};
|
||||||
|
|
||||||
|
var drawFavicon = function(num, colour) {
|
||||||
|
|
||||||
|
// fallback to updating the browser title if unsupported
|
||||||
|
if (!getCanvas().getContext || browser.ie || browser.safari || options.fallback === 'force') {
|
||||||
|
return updateTitle(num);
|
||||||
|
}
|
||||||
|
|
||||||
|
var context = getCanvas().getContext("2d");
|
||||||
|
var colour = colour || '#000000';
|
||||||
|
var num = num || 0;
|
||||||
|
var src = getCurrentFavicon();
|
||||||
|
|
||||||
|
faviconImage = new Image();
|
||||||
|
faviconImage.onload = function() {
|
||||||
|
|
||||||
|
// clear canvas
|
||||||
|
context.clearRect(0, 0, 16, 16);
|
||||||
|
|
||||||
|
// draw original favicon
|
||||||
|
context.drawImage(faviconImage, 0, 0, faviconImage.width, faviconImage.height, 0, 0, 16, 16);
|
||||||
|
|
||||||
|
// draw bubble over the top
|
||||||
|
if (num > 0) drawBubble(context, num, colour);
|
||||||
|
|
||||||
|
// refresh tag in page
|
||||||
|
refreshFavicon();
|
||||||
|
};
|
||||||
|
|
||||||
|
// allow cross origin resource requests if the image is not a data:uri
|
||||||
|
// as detailed here: https://github.com/mrdoob/three.js/issues/1305
|
||||||
|
if (!src.match(/^data/)) {
|
||||||
|
faviconImage.crossOrigin = 'anonymous';
|
||||||
|
}
|
||||||
|
|
||||||
|
faviconImage.src = src;
|
||||||
|
};
|
||||||
|
|
||||||
|
var updateTitle = function(num) {
|
||||||
|
|
||||||
|
if (options.fallback) {
|
||||||
|
if (num > 0) {
|
||||||
|
document.title = '('+num+') ' + originalTitle;
|
||||||
|
} else {
|
||||||
|
document.title = originalTitle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var drawBubble = function(context, num, colour) {
|
||||||
|
|
||||||
|
// bubble needs to be larger for double digits
|
||||||
|
var len = (num+"").length-1;
|
||||||
|
var width = options.width + (6*len);
|
||||||
|
var w = 16-width;
|
||||||
|
var h = 16-options.height;
|
||||||
|
|
||||||
|
// webkit seems to render fonts lighter than firefox
|
||||||
|
context.font = (browser.webkit ? 'bold ' : '') + options.font;
|
||||||
|
context.fillStyle = options.background;
|
||||||
|
context.strokeStyle = options.background;
|
||||||
|
context.lineWidth = 1;
|
||||||
|
|
||||||
|
// bubble
|
||||||
|
context.fillRect(w,h,width-1,options.height);
|
||||||
|
|
||||||
|
// rounded left
|
||||||
|
context.beginPath();
|
||||||
|
context.moveTo(w-0.5,h+1);
|
||||||
|
context.lineTo(w-0.5,15);
|
||||||
|
context.stroke();
|
||||||
|
|
||||||
|
// rounded right
|
||||||
|
context.beginPath();
|
||||||
|
context.moveTo(15.5,h+1);
|
||||||
|
context.lineTo(15.5,15);
|
||||||
|
context.stroke();
|
||||||
|
|
||||||
|
// bottom shadow
|
||||||
|
context.beginPath();
|
||||||
|
context.strokeStyle = "rgba(0,0,0,0.3)";
|
||||||
|
context.moveTo(w,16);
|
||||||
|
context.lineTo(15,16);
|
||||||
|
context.stroke();
|
||||||
|
|
||||||
|
// number
|
||||||
|
context.fillStyle = options.colour;
|
||||||
|
context.textAlign = "right";
|
||||||
|
context.textBaseline = "top";
|
||||||
|
|
||||||
|
// unfortunately webkit/mozilla are a pixel different in text positioning
|
||||||
|
context.fillText(num, 15, browser.mozilla ? 7 : 6);
|
||||||
|
};
|
||||||
|
|
||||||
|
var refreshFavicon = function(){
|
||||||
|
// check support
|
||||||
|
if (!getCanvas().getContext) return;
|
||||||
|
|
||||||
|
setFaviconTag(getCanvas().toDataURL());
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// public methods
|
||||||
|
Tinycon.setOptions = function(custom){
|
||||||
|
options = {};
|
||||||
|
|
||||||
|
for(var key in defaults){
|
||||||
|
options[key] = custom.hasOwnProperty(key) ? custom[key] : defaults[key];
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
Tinycon.setImage = function(url){
|
||||||
|
currentFavicon = url;
|
||||||
|
refreshFavicon();
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
Tinycon.setBubble = function(num, colour){
|
||||||
|
|
||||||
|
// validate
|
||||||
|
if(isNaN(parseFloat(num)) || !isFinite(num)) return log('Bubble must be a number');
|
||||||
|
|
||||||
|
drawFavicon(num, colour);
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
Tinycon.reset = function(){
|
||||||
|
Tinycon.setImage(originalFavicon);
|
||||||
|
};
|
||||||
|
|
||||||
|
Tinycon.setOptions(defaults);
|
||||||
|
window.Tinycon = Tinycon;
|
||||||
|
})();
|
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
<h1>Etherpad Lite</h1>
|
<h1>Etherpad Lite</h1>
|
||||||
|
|
||||||
<a href="/admin/plugins/info">Technical information on installed plugins</a>
|
<a href="plugins/info">Technical information on installed plugins</a>
|
||||||
|
|
||||||
<div class="separator"></div>
|
<div class="separator"></div>
|
||||||
<h2>Installed plugins</h2>
|
<h2>Installed plugins</h2>
|
||||||
|
|
|
@ -148,7 +148,10 @@
|
||||||
|
|
||||||
<div id="editorcontainerbox">
|
<div id="editorcontainerbox">
|
||||||
<div id="editorcontainer"></div>
|
<div id="editorcontainer"></div>
|
||||||
<div id="editorloadingbox">Loading...</div>
|
<div id="editorloadingbox">
|
||||||
|
<p>Loading...</p>
|
||||||
|
<noscript><strong>Sorry, you have to enable Javascript in order to use this.</strong></noscript>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="settings" class="popup">
|
<div id="settings" class="popup">
|
||||||
|
@ -313,6 +316,19 @@
|
||||||
<% e.end_block(); %>
|
<% e.end_block(); %>
|
||||||
|
|
||||||
<% e.begin_block("scripts"); %>
|
<% e.begin_block("scripts"); %>
|
||||||
|
<script type="text/javascript">
|
||||||
|
/* Display errors on page load to the user
|
||||||
|
(Gets overridden by padutils.setupGlobalExceptionHandler)
|
||||||
|
*/
|
||||||
|
window.onerror = function(msg, url, line) {
|
||||||
|
console.log('error', arguments);
|
||||||
|
var box = document.getElementById('editorloadingbox');
|
||||||
|
box.innerHTML = '<p><b>An error occured while loading the pad</b></p>'
|
||||||
|
+ '<p><b>'+msg+'</b> '
|
||||||
|
+ '<small>in '+ url +' (line '+ line +')</small></p>'
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
<script type="text/javascript" src="../static/js/require-kernel.js"></script>
|
<script type="text/javascript" src="../static/js/require-kernel.js"></script>
|
||||||
<script type="text/javascript" src="../socket.io/socket.io.js"></script>
|
<script type="text/javascript" src="../socket.io/socket.io.js"></script>
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue