mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-05-04 22:27:10 -04:00
Merge git://github.com/Pita/etherpad-lite
This commit is contained in:
commit
33266915be
21 changed files with 305 additions and 103 deletions
22
README.md
22
README.md
|
@ -9,8 +9,14 @@ documented codebase makes it easier for developers to improve the code and contr
|
||||||
|
|
||||||
Etherpad Lite is optimized to be easy embeddable. It provides a [HTTP API](https://github.com/Pita/etherpad-lite/wiki/HTTP-API)
|
Etherpad Lite is optimized to be easy embeddable. It provides a [HTTP API](https://github.com/Pita/etherpad-lite/wiki/HTTP-API)
|
||||||
that allows your web application to manage pads, users and groups.
|
that allows your web application to manage pads, users and groups.
|
||||||
You can use this [PHP Client](https://github.com/TomNomNom/etherpad-lite-client) to work with the API
|
There are several clients in for this API:
|
||||||
(If you don't want to use PHP, feel free to create a client for your favourite web development language).
|
|
||||||
|
* [PHP](https://github.com/TomNomNom/etherpad-lite-client), thx to [TomNomNom](https://github.com/TomNomNom)
|
||||||
|
* [.Net](https://github.com/ja-jo/EtherpadLiteDotNet), thx to [ja-jo](https://github.com/ja-jo)
|
||||||
|
* [Node.js](https://github.com/tomassedovic/etherpad-lite-client-js), thx to [tomassedovic](https://github.com/tomassedovic)
|
||||||
|
* [Ruby](https://github.com/jhollinger/ruby-etherpad-lite), thx to [jhollinger](https://github.com/jhollinger)
|
||||||
|
* [Python](https://github.com/devjones/PyEtherpadLite), thx to [devjones](https://github.com/devjones)
|
||||||
|
|
||||||
There is also a [jQuery plugin](https://github.com/johnyma22/etherpad-lite-jquery-plugin) that helps you to embed Pads into your website
|
There is also a [jQuery plugin](https://github.com/johnyma22/etherpad-lite-jquery-plugin) that helps you to embed Pads into your website
|
||||||
|
|
||||||
**Online demo**<br>
|
**Online demo**<br>
|
||||||
|
@ -51,12 +57,14 @@ Here is the **[FAQ](https://github.com/Pita/etherpad-lite/wiki/FAQ)**
|
||||||
**As root:**
|
**As root:**
|
||||||
|
|
||||||
<ol>
|
<ol>
|
||||||
<li>Install the dependencies. We need the gzip, git, curl, libssl develop libraries and python <br><code>apt-get install gzip git-core curl python libssl-dev</code></li><br>
|
<li>Install the dependencies. We need gzip, git, curl, libssl develop libraries, python and gcc. <br><i>For Debian/Ubuntu</i> <code>apt-get install gzip git-core curl python libssl-dev build-essential</code><br>
|
||||||
|
<i>For Fedora/CentOS</i> <code>yum install gzip git-core curl python openssl-dev && yum groupinstall "Development Tools"</code>
|
||||||
|
</li><br>
|
||||||
<li>Install node.js
|
<li>Install node.js
|
||||||
<ol type="a">
|
<ol type="a">
|
||||||
<li>Download the latest <b>0.4.x</b> node.js release from <a href="http://nodejs.org/#download">http://nodejs.org/#download</a></li>
|
<li>Download the latest <b>0.6.x</b> node.js release from <a href="http://nodejs.org/#download">http://nodejs.org/#download</a></li>
|
||||||
<li>Extract it with <code>tar xf node-v0.4*</code></li>
|
<li>Extract it with <code>tar xf node-v0.6*</code></li>
|
||||||
<li>Move into the node folder <code>cd node-v0.4*</code> and build node with <code>./configure && make && make install</code></li>
|
<li>Move into the node folder <code>cd node-v0.6*</code> and build node with <code>./configure && make && make install</code></li>
|
||||||
</ol>
|
</ol>
|
||||||
</li>
|
</li>
|
||||||
<li>Install npm <code>curl http://npmjs.org/install.sh | sh</code></li>
|
<li>Install npm <code>curl http://npmjs.org/install.sh | sh</code></li>
|
||||||
|
@ -74,6 +82,8 @@ Here is the **[FAQ](https://github.com/Pita/etherpad-lite/wiki/FAQ)**
|
||||||
## Next Steps
|
## Next Steps
|
||||||
You can modify the settings in the file `settings.json`
|
You can modify the settings in the file `settings.json`
|
||||||
|
|
||||||
|
You should use a dedicated database such as "mysql" if you are planning on using etherpad-lite in a production environment, the "dirty" database driver is only for testing and/or development purposes.
|
||||||
|
|
||||||
You can update to the latest version with `git pull origin`. The next start with bin/run.sh will update the dependencies
|
You can update to the latest version with `git pull origin`. The next start with bin/run.sh will update the dependencies
|
||||||
|
|
||||||
Look at this wiki pages:
|
Look at this wiki pages:
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
NODE_VERSION="0.5.4"
|
NODE_VERSION="0.6.1"
|
||||||
|
|
||||||
#Move to the folder where ep-lite is installed
|
#Move to the folder where ep-lite is installed
|
||||||
cd `dirname $0`
|
cd `dirname $0`
|
||||||
|
@ -48,16 +48,6 @@ cp -rL node_modules node_modules_resolved
|
||||||
rm -rf node_modules
|
rm -rf node_modules
|
||||||
mv node_modules_resolved node_modules
|
mv node_modules_resolved node_modules
|
||||||
|
|
||||||
echo "remove sqlite, cause we can't use it with windows..."
|
|
||||||
rm -rf node_modules/ueberDB/node_modules/sqlite3
|
|
||||||
|
|
||||||
echo "replace log4js with a patched log4js, this log4js runs on windows too..."
|
|
||||||
rm -rf node_modules/log4js/*
|
|
||||||
wget https://github.com/Pita/log4js-node/zipball/master -O log4js.zip
|
|
||||||
unzip log4js.zip
|
|
||||||
mv Pita-log4js-node*/* node_modules/log4js
|
|
||||||
rm -rf log4js.zip Pita-log4js-node*
|
|
||||||
|
|
||||||
echo "download windows node..."
|
echo "download windows node..."
|
||||||
cd bin
|
cd bin
|
||||||
wget "http://nodejs.org/dist/v$NODE_VERSION/node.exe" -O node.exe
|
wget "http://nodejs.org/dist/v$NODE_VERSION/node.exe" -O node.exe
|
||||||
|
|
|
@ -20,13 +20,6 @@ hash node > /dev/null 2>&1 || {
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
#check node version
|
|
||||||
NODE_VERSION=$(node --version)
|
|
||||||
if [ ! $(echo $NODE_VERSION | cut -d "." -f 1-2) = "v0.4" ]; then
|
|
||||||
echo "You're running a wrong version of node, you're using $NODE_VERSION, we need v0.4.x" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
#Is npm installed?
|
#Is npm installed?
|
||||||
hash npm > /dev/null 2>&1 || {
|
hash npm > /dev/null 2>&1 || {
|
||||||
echo "Please install npm ( http://npmjs.org )" >&2
|
echo "Please install npm ( http://npmjs.org )" >&2
|
||||||
|
@ -54,9 +47,9 @@ npm install || {
|
||||||
|
|
||||||
echo "Ensure jQuery is downloaded and up to date..."
|
echo "Ensure jQuery is downloaded and up to date..."
|
||||||
DOWNLOAD_JQUERY="true"
|
DOWNLOAD_JQUERY="true"
|
||||||
NEEDED_VERSION="1.6.2"
|
NEEDED_VERSION="1.7"
|
||||||
if [ -f "static/js/jquery.min.js" ]; then
|
if [ -f "static/js/jquery.min.js" ]; then
|
||||||
VERSION=$(cat static/js/jquery.min.js | head -n 2 | tail -n 1 | grep -o "v[0-9]*\.[0-9]*\.[0-9]*");
|
VERSION=$(cat static/js/jquery.min.js | head -n 1 | grep -o "v[0-9].[0-9]");
|
||||||
|
|
||||||
if [ ${VERSION#v} = $NEEDED_VERSION ]; then
|
if [ ${VERSION#v} = $NEEDED_VERSION ]; then
|
||||||
DOWNLOAD_JQUERY="false"
|
DOWNLOAD_JQUERY="false"
|
||||||
|
|
|
@ -25,6 +25,7 @@ var groupManager = require("./GroupManager");
|
||||||
var authorManager = require("./AuthorManager");
|
var authorManager = require("./AuthorManager");
|
||||||
var sessionManager = require("./SessionManager");
|
var sessionManager = require("./SessionManager");
|
||||||
var async = require("async");
|
var async = require("async");
|
||||||
|
var exportHtml = require("../utils/ExportHtml");
|
||||||
|
|
||||||
/**********************/
|
/**********************/
|
||||||
/**GROUP FUNCTIONS*****/
|
/**GROUP FUNCTIONS*****/
|
||||||
|
@ -169,6 +170,90 @@ exports.setText = function(padID, text, callback)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
getHTML(padID, [rev]) returns the html of a pad
|
||||||
|
|
||||||
|
Example returns:
|
||||||
|
|
||||||
|
{code: 0, message:"ok", data: {text:"Welcome <strong>Text</strong>"}}
|
||||||
|
{code: 1, message:"padID does not exist", data: null}
|
||||||
|
*/
|
||||||
|
exports.getHTML = function(padID, rev, callback)
|
||||||
|
{
|
||||||
|
if(typeof rev == "function")
|
||||||
|
{
|
||||||
|
callback = rev;
|
||||||
|
rev = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rev !== undefined && typeof rev != "number")
|
||||||
|
{
|
||||||
|
if (!isNaN(parseInt(rev)))
|
||||||
|
{
|
||||||
|
rev = parseInt(rev);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
callback({stop: "rev is not a number"});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(rev !== undefined && rev < 0)
|
||||||
|
{
|
||||||
|
callback({stop: "rev is a negative number"});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(rev !== undefined && !is_int(rev))
|
||||||
|
{
|
||||||
|
callback({stop: "rev is a float value"});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPadSafe(padID, true, function(err, pad)
|
||||||
|
{
|
||||||
|
if(err)
|
||||||
|
{
|
||||||
|
callback(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//the client asked for a special revision
|
||||||
|
if(rev !== undefined)
|
||||||
|
{
|
||||||
|
//check if this is a valid revision
|
||||||
|
if(rev > pad.getHeadRevisionNumber())
|
||||||
|
{
|
||||||
|
callback({stop: "rev is higher than the head revision of the pad"});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the html of this revision
|
||||||
|
exportHtml.getPadHTML(pad, rev, function(err, html)
|
||||||
|
{
|
||||||
|
if(!err)
|
||||||
|
{
|
||||||
|
data = {html: html};
|
||||||
|
}
|
||||||
|
callback(err, data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//the client wants the latest text, lets return it to him
|
||||||
|
else
|
||||||
|
{
|
||||||
|
exportHtml.getPadHTML(pad, undefined, function (err, html)
|
||||||
|
{
|
||||||
|
if(!err)
|
||||||
|
{
|
||||||
|
data = {html: html};
|
||||||
|
}
|
||||||
|
callback(err, data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/*****************/
|
/*****************/
|
||||||
/**PAD FUNCTIONS */
|
/**PAD FUNCTIONS */
|
||||||
/*****************/
|
/*****************/
|
||||||
|
|
|
@ -22,9 +22,19 @@ require("../db/Pad");
|
||||||
var db = require("./DB").db;
|
var db = require("./DB").db;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Array with all known Pads
|
* An Object containing all known Pads. Provides "get" and "set" functions,
|
||||||
|
* which should be used instead of indexing with brackets. These prepend a
|
||||||
|
* colon to the key, to avoid conflicting with built-in Object methods or with
|
||||||
|
* these functions themselves.
|
||||||
|
*
|
||||||
|
* If this is needed in other places, it would be wise to make this a prototype
|
||||||
|
* that's defined somewhere more sensible.
|
||||||
*/
|
*/
|
||||||
globalPads = [];
|
globalPads = {
|
||||||
|
get: function (name) { return this[':'+name]; },
|
||||||
|
set: function (name, value) { this[':'+name] = value; },
|
||||||
|
remove: function (name) { delete this[':'+name]; }
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a Pad Object with the callback
|
* Returns a Pad Object with the callback
|
||||||
|
@ -65,7 +75,7 @@ exports.getPad = function(id, text, callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var pad = globalPads[id];
|
var pad = globalPads.get(id);
|
||||||
|
|
||||||
//return pad if its already loaded
|
//return pad if its already loaded
|
||||||
if(pad != null)
|
if(pad != null)
|
||||||
|
@ -86,7 +96,7 @@ exports.getPad = function(id, text, callback)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
globalPads[id] = pad;
|
globalPads.set(id, pad);
|
||||||
callback(null, pad);
|
callback(null, pad);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -110,6 +120,6 @@ exports.isValidPadId = function(padId)
|
||||||
//removes a pad from the array
|
//removes a pad from the array
|
||||||
exports.unloadPad = function(padId)
|
exports.unloadPad = function(padId)
|
||||||
{
|
{
|
||||||
if(globalPads[padId])
|
if(globalPads.get(padId))
|
||||||
delete globalPads[padId];
|
globalPads.remove(padId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,7 @@ var functions = {
|
||||||
"listSessionsOfAuthor" : ["authorID"],
|
"listSessionsOfAuthor" : ["authorID"],
|
||||||
"getText" : ["padID", "rev"],
|
"getText" : ["padID", "rev"],
|
||||||
"setText" : ["padID", "text"],
|
"setText" : ["padID", "text"],
|
||||||
|
"getHTML" : ["padID", "rev"],
|
||||||
"getRevisionsCount" : ["padID"],
|
"getRevisionsCount" : ["padID"],
|
||||||
"deletePad" : ["padID"],
|
"deletePad" : ["padID"],
|
||||||
"getReadOnlyID" : ["padID"],
|
"getReadOnlyID" : ["padID"],
|
||||||
|
|
|
@ -81,10 +81,9 @@ exports.doExport = function(req, res, padId, type)
|
||||||
res.send(html);
|
res.send(html);
|
||||||
callback("stop");
|
callback("stop");
|
||||||
}
|
}
|
||||||
//write the html export to a file
|
else //write the html export to a file
|
||||||
else
|
|
||||||
{
|
{
|
||||||
randNum = Math.floor(Math.random()*new Date().getTime());
|
randNum = Math.floor(Math.random()*0xFFFFFFFF);
|
||||||
srcFile = tempDirectory + "/eplite_export_" + randNum + ".html";
|
srcFile = tempDirectory + "/eplite_export_" + randNum + ".html";
|
||||||
fs.writeFile(srcFile, html, callback);
|
fs.writeFile(srcFile, html, callback);
|
||||||
}
|
}
|
||||||
|
@ -114,11 +113,14 @@ exports.doExport = function(req, res, padId, type)
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
//100ms delay to accomidate for slow windows fs
|
//100ms delay to accomidate for slow windows fs
|
||||||
|
if(os.type().indexOf("Windows") > -1)
|
||||||
|
{
|
||||||
setTimeout(function()
|
setTimeout(function()
|
||||||
{
|
{
|
||||||
fs.unlink(destFile, callback);
|
fs.unlink(destFile, callback);
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
], callback);
|
], callback);
|
||||||
}
|
}
|
||||||
], function(err)
|
], function(err)
|
||||||
|
|
|
@ -60,11 +60,20 @@ exports.doImport = function(req, res, padId)
|
||||||
form.uploadDir = tempDirectory;
|
form.uploadDir = tempDirectory;
|
||||||
|
|
||||||
form.parse(req, function(err, fields, files)
|
form.parse(req, function(err, fields, files)
|
||||||
|
{
|
||||||
|
//the upload failed, stop at this point
|
||||||
|
if(err || files.file === undefined)
|
||||||
|
{
|
||||||
|
console.warn("Uploading Error: " + err.stack);
|
||||||
|
callback("uploadFailed");
|
||||||
|
}
|
||||||
|
//everything ok, continue
|
||||||
|
else
|
||||||
{
|
{
|
||||||
//save the path of the uploaded file
|
//save the path of the uploaded file
|
||||||
srcFile = files.file.path;
|
srcFile = files.file.path;
|
||||||
|
callback();
|
||||||
callback(err);
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -157,6 +166,13 @@ exports.doImport = function(req, res, padId)
|
||||||
}
|
}
|
||||||
], function(err)
|
], function(err)
|
||||||
{
|
{
|
||||||
|
//the upload failed, there is nothing we can do, send a 500
|
||||||
|
if(err == "uploadFailed")
|
||||||
|
{
|
||||||
|
res.send(500);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if(err) throw err;
|
if(err) throw err;
|
||||||
|
|
||||||
//close the connection
|
//close the connection
|
||||||
|
|
|
@ -136,7 +136,7 @@ exports.handleDisconnect = function(client)
|
||||||
{
|
{
|
||||||
if(pad2sessions[sessionPad][i] == client.id)
|
if(pad2sessions[sessionPad][i] == client.id)
|
||||||
{
|
{
|
||||||
delete pad2sessions[sessionPad][i];
|
pad2sessions[sessionPad].splice(i, 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -190,10 +190,10 @@ exports.handleMessage = function(client, message)
|
||||||
{
|
{
|
||||||
handleSuggestUserName(client, message);
|
handleSuggestUserName(client, message);
|
||||||
}
|
}
|
||||||
//if the message type is unkown, throw an exception
|
//if the message type is unknown, throw an exception
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
messageLogger.warn("Droped message, unkown Message Type " + message.type);
|
messageLogger.warn("Dropped message, unknown Message Type " + message.type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,12 +272,12 @@ function handleSuggestUserName(client, message)
|
||||||
//check if all ok
|
//check if all ok
|
||||||
if(message.data.payload.newName == null)
|
if(message.data.payload.newName == null)
|
||||||
{
|
{
|
||||||
messageLogger.warn("Droped message, suggestUserName Message has no newName!");
|
messageLogger.warn("Dropped message, suggestUserName Message has no newName!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(message.data.payload.unnamedId == null)
|
if(message.data.payload.unnamedId == null)
|
||||||
{
|
{
|
||||||
messageLogger.warn("Droped message, suggestUserName Message has no unnamedId!");
|
messageLogger.warn("Dropped message, suggestUserName Message has no unnamedId!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,7 +304,7 @@ function handleUserInfoUpdate(client, message)
|
||||||
//check if all ok
|
//check if all ok
|
||||||
if(message.data.userInfo.colorId == null)
|
if(message.data.userInfo.colorId == null)
|
||||||
{
|
{
|
||||||
messageLogger.warn("Droped message, USERINFO_UPDATE Message has no colorId!");
|
messageLogger.warn("Dropped message, USERINFO_UPDATE Message has no colorId!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -348,17 +348,17 @@ function handleUserChanges(client, message)
|
||||||
//check if all ok
|
//check if all ok
|
||||||
if(message.data.baseRev == null)
|
if(message.data.baseRev == null)
|
||||||
{
|
{
|
||||||
messageLogger.warn("Droped message, USER_CHANGES Message has no baseRev!");
|
messageLogger.warn("Dropped message, USER_CHANGES Message has no baseRev!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(message.data.apool == null)
|
if(message.data.apool == null)
|
||||||
{
|
{
|
||||||
messageLogger.warn("Droped message, USER_CHANGES Message has no apool!");
|
messageLogger.warn("Dropped message, USER_CHANGES Message has no apool!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(message.data.changeset == null)
|
if(message.data.changeset == null)
|
||||||
{
|
{
|
||||||
messageLogger.warn("Droped message, USER_CHANGES Message has no changeset!");
|
messageLogger.warn("Dropped message, USER_CHANGES Message has no changeset!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -600,22 +600,22 @@ function handleClientReady(client, message)
|
||||||
//check if all ok
|
//check if all ok
|
||||||
if(!message.token)
|
if(!message.token)
|
||||||
{
|
{
|
||||||
messageLogger.warn("Droped message, CLIENT_READY Message has no token!");
|
messageLogger.warn("Dropped message, CLIENT_READY Message has no token!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(!message.padId)
|
if(!message.padId)
|
||||||
{
|
{
|
||||||
messageLogger.warn("Droped message, CLIENT_READY Message has no padId!");
|
messageLogger.warn("Dropped message, CLIENT_READY Message has no padId!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(!message.protocolVersion)
|
if(!message.protocolVersion)
|
||||||
{
|
{
|
||||||
messageLogger.warn("Droped message, CLIENT_READY Message has no protocolVersion!");
|
messageLogger.warn("Dropped message, CLIENT_READY Message has no protocolVersion!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(message.protocolVersion != 2)
|
if(message.protocolVersion != 2)
|
||||||
{
|
{
|
||||||
messageLogger.warn("Droped message, CLIENT_READY Message has a unkown protocolVersion '" + message.protocolVersion + "'!");
|
messageLogger.warn("Dropped message, CLIENT_READY Message has a unknown protocolVersion '" + message.protocolVersion + "'!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -128,7 +128,7 @@ exports.setSocketIO = function(_socket)
|
||||||
//drop message
|
//drop message
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
messageLogger.warn("Droped message cause of bad permissions:" + stringifyWithoutPassword(message));
|
messageLogger.warn("Dropped message cause of bad permissions:" + stringifyWithoutPassword(message));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -77,7 +77,7 @@ exports.handleMessage = function(client, message)
|
||||||
//if the message type is unkown, throw an exception
|
//if the message type is unkown, throw an exception
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
messageLogger.warn("Droped message, unkown Message Type: '" + message.type + "'");
|
messageLogger.warn("Dropped message, unknown Message Type: '" + message.type + "'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ function handleClientReady(client, message)
|
||||||
{
|
{
|
||||||
if(message.padId == null)
|
if(message.padId == null)
|
||||||
{
|
{
|
||||||
messageLogger.warn("Droped message, changeset request has no padId!");
|
messageLogger.warn("Dropped message, changeset request has no padId!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,27 +106,27 @@ function handleChangesetRequest(client, message)
|
||||||
//check if all ok
|
//check if all ok
|
||||||
if(message.data == null)
|
if(message.data == null)
|
||||||
{
|
{
|
||||||
messageLogger.warn("Droped message, changeset request has no data!");
|
messageLogger.warn("Dropped message, changeset request has no data!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(message.padId == null)
|
if(message.padId == null)
|
||||||
{
|
{
|
||||||
messageLogger.warn("Droped message, changeset request has no padId!");
|
messageLogger.warn("Dropped message, changeset request has no padId!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(message.data.granularity == null)
|
if(message.data.granularity == null)
|
||||||
{
|
{
|
||||||
messageLogger.warn("Droped message, changeset request has no granularity!");
|
messageLogger.warn("Dropped message, changeset request has no granularity!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(message.data.start == null)
|
if(message.data.start == null)
|
||||||
{
|
{
|
||||||
messageLogger.warn("Droped message, changeset request has no start!");
|
messageLogger.warn("Dropped message, changeset request has no start!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(message.data.requestID == null)
|
if(message.data.requestID == null)
|
||||||
{
|
{
|
||||||
messageLogger.warn("Droped message, changeset request has no requestID!");
|
messageLogger.warn("Dropped message, changeset request has no requestID!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -91,6 +91,9 @@ async.waterfall([
|
||||||
var httpLogger = log4js.getLogger("http");
|
var httpLogger = log4js.getLogger("http");
|
||||||
app.configure(function()
|
app.configure(function()
|
||||||
{
|
{
|
||||||
|
// If the log level specified in the config file is WARN or ERROR the application server never starts listening to requests as reported in issue #158.
|
||||||
|
// Not installing the log4js connect logger when the log level has a higher severity than INFO since it would not log at that level anyway.
|
||||||
|
if (!(settings.loglevel === "WARN" || settings.loglevel == "ERROR"))
|
||||||
app.use(log4js.connectLogger(httpLogger, { level: log4js.levels.INFO, format: ':status, :method :url'}));
|
app.use(log4js.connectLogger(httpLogger, { level: log4js.levels.INFO, format: ':status, :method :url'}));
|
||||||
app.use(express.cookieParser());
|
app.use(express.cookieParser());
|
||||||
});
|
});
|
||||||
|
@ -288,13 +291,12 @@ async.waterfall([
|
||||||
|
|
||||||
var apiLogger = log4js.getLogger("API");
|
var apiLogger = log4js.getLogger("API");
|
||||||
|
|
||||||
//This is a api call, collect all post informations and pass it to the apiHandler
|
//This is for making an api call, collecting all post information and passing it to the apiHandler
|
||||||
app.get('/api/1/:func', function(req, res)
|
var apiCaller = function(req, res, fields) {
|
||||||
{
|
|
||||||
res.header("Server", serverName);
|
res.header("Server", serverName);
|
||||||
res.header("Content-Type", "application/json; charset=utf-8");
|
res.header("Content-Type", "application/json; charset=utf-8");
|
||||||
|
|
||||||
apiLogger.info("REQUEST, " + req.params.func + ", " + JSON.stringify(req.query));
|
apiLogger.info("REQUEST, " + req.params.func + ", " + JSON.stringify(fields));
|
||||||
|
|
||||||
//wrap the send function so we can log the response
|
//wrap the send function so we can log the response
|
||||||
res._send = res.send;
|
res._send = res.send;
|
||||||
|
@ -311,7 +313,22 @@ async.waterfall([
|
||||||
}
|
}
|
||||||
|
|
||||||
//call the api handler
|
//call the api handler
|
||||||
apiHandler.handle(req.params.func, req.query, req, res);
|
apiHandler.handle(req.params.func, fields, req, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
//This is a api GET call, collect all post informations and pass it to the apiHandler
|
||||||
|
app.get('/api/1/:func', function(req, res)
|
||||||
|
{
|
||||||
|
apiCaller(req, res, req.query)
|
||||||
|
});
|
||||||
|
|
||||||
|
//This is a api POST call, collect all post informations and pass it to the apiHandler
|
||||||
|
app.post('/api/1/:func', function(req, res)
|
||||||
|
{
|
||||||
|
new formidable.IncomingForm().parse(req, function(err, fields, files)
|
||||||
|
{
|
||||||
|
apiCaller(req, res, fields)
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
//The Etherpad client side sends information about how a disconnect happen
|
//The Etherpad client side sends information about how a disconnect happen
|
||||||
|
@ -419,25 +436,25 @@ async.waterfall([
|
||||||
|
|
||||||
//this is only a workaround to ensure it works with all browers behind a proxy
|
//this is only a workaround to ensure it works with all browers behind a proxy
|
||||||
//we should remove this when the new socket.io version is more stable
|
//we should remove this when the new socket.io version is more stable
|
||||||
io.set('transports', ['xhr-polling']);
|
//io.set('transports', ['xhr-polling']);
|
||||||
|
|
||||||
var socketIOLogger = log4js.getLogger("socket.io");
|
var socketIOLogger = log4js.getLogger("socket.io");
|
||||||
io.set('logger', {
|
io.set('logger', {
|
||||||
debug: function (str)
|
debug: function (str)
|
||||||
{
|
{
|
||||||
socketIOLogger.debug(str);
|
socketIOLogger.debug.apply(socketIOLogger, arguments);
|
||||||
},
|
},
|
||||||
info: function (str)
|
info: function (str)
|
||||||
{
|
{
|
||||||
socketIOLogger.info(str);
|
socketIOLogger.info.apply(socketIOLogger, arguments);
|
||||||
},
|
},
|
||||||
warn: function (str)
|
warn: function (str)
|
||||||
{
|
{
|
||||||
socketIOLogger.warn(str);
|
socketIOLogger.warn.apply(socketIOLogger, arguments);
|
||||||
},
|
},
|
||||||
error: function (str)
|
error: function (str)
|
||||||
{
|
{
|
||||||
socketIOLogger.error(str);
|
socketIOLogger.error.apply(socketIOLogger, arguments);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -85,6 +85,8 @@ function getPadHTML(pad, revNum, callback)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.getPadHTML = getPadHTML;
|
||||||
|
|
||||||
function getHTMLFromAtext(pad, atext)
|
function getHTMLFromAtext(pad, atext)
|
||||||
{
|
{
|
||||||
var apool = pad.apool();
|
var apool = pad.apool();
|
||||||
|
@ -119,8 +121,10 @@ function getHTMLFromAtext(pad, atext)
|
||||||
var taker = Changeset.stringIterator(text);
|
var taker = Changeset.stringIterator(text);
|
||||||
var assem = Changeset.stringAssembler();
|
var assem = Changeset.stringAssembler();
|
||||||
|
|
||||||
|
var openTags = [];
|
||||||
function emitOpenTag(i)
|
function emitOpenTag(i)
|
||||||
{
|
{
|
||||||
|
openTags.unshift(i);
|
||||||
assem.append('<');
|
assem.append('<');
|
||||||
assem.append(tags[i]);
|
assem.append(tags[i]);
|
||||||
assem.append('>');
|
assem.append('>');
|
||||||
|
@ -128,11 +132,28 @@ function getHTMLFromAtext(pad, atext)
|
||||||
|
|
||||||
function emitCloseTag(i)
|
function emitCloseTag(i)
|
||||||
{
|
{
|
||||||
|
openTags.shift();
|
||||||
assem.append('</');
|
assem.append('</');
|
||||||
assem.append(tags[i]);
|
assem.append(tags[i]);
|
||||||
assem.append('>');
|
assem.append('>');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function orderdCloseTags(tags2close)
|
||||||
|
{
|
||||||
|
for(var i=0;i<openTags.length;i++)
|
||||||
|
{
|
||||||
|
for(var j=0;j<tags2close.length;j++)
|
||||||
|
{
|
||||||
|
if(tags2close[j] == openTags[i])
|
||||||
|
{
|
||||||
|
emitCloseTag(tags2close[j]);
|
||||||
|
i--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var urls = _findURLs(text);
|
var urls = _findURLs(text);
|
||||||
|
|
||||||
var idx = 0;
|
var idx = 0;
|
||||||
|
@ -204,18 +225,25 @@ function getHTMLFromAtext(pad, atext)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var tags2close = [];
|
||||||
|
|
||||||
for (var i = propVals.length - 1; i >= 0; i--)
|
for (var i = propVals.length - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
if (propVals[i] === LEAVE)
|
if (propVals[i] === LEAVE)
|
||||||
{
|
{
|
||||||
emitCloseTag(i);
|
//emitCloseTag(i);
|
||||||
|
tags2close.push(i);
|
||||||
propVals[i] = false;
|
propVals[i] = false;
|
||||||
}
|
}
|
||||||
else if (propVals[i] === STAY)
|
else if (propVals[i] === STAY)
|
||||||
{
|
{
|
||||||
emitCloseTag(i);
|
//emitCloseTag(i);
|
||||||
|
tags2close.push(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
orderdCloseTags(tags2close);
|
||||||
|
|
||||||
for (var i = 0; i < propVals.length; i++)
|
for (var i = 0; i < propVals.length; i++)
|
||||||
{
|
{
|
||||||
if (propVals[i] === ENTER || propVals[i] === STAY)
|
if (propVals[i] === ENTER || propVals[i] === STAY)
|
||||||
|
@ -231,18 +259,27 @@ function getHTMLFromAtext(pad, atext)
|
||||||
{
|
{
|
||||||
chars--; // exclude newline at end of line, if present
|
chars--; // exclude newline at end of line, if present
|
||||||
}
|
}
|
||||||
|
|
||||||
var s = taker.take(chars);
|
var s = taker.take(chars);
|
||||||
|
|
||||||
|
//removes the characters with the code 12. Don't know where they come
|
||||||
|
//from but they break the abiword parser and are completly useless
|
||||||
|
s = s.replace(String.fromCharCode(12), "");
|
||||||
|
|
||||||
assem.append(_escapeHTML(s));
|
assem.append(_escapeHTML(s));
|
||||||
} // end iteration over spans in line
|
} // end iteration over spans in line
|
||||||
|
|
||||||
|
var tags2close = [];
|
||||||
for (var i = propVals.length - 1; i >= 0; i--)
|
for (var i = propVals.length - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
if (propVals[i])
|
if (propVals[i])
|
||||||
{
|
{
|
||||||
emitCloseTag(i);
|
tags2close.push(i);
|
||||||
propVals[i] = false;
|
propVals[i] = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
orderdCloseTags(tags2close);
|
||||||
} // end processNextChars
|
} // end processNextChars
|
||||||
if (urls)
|
if (urls)
|
||||||
{
|
{
|
||||||
|
|
18
package.json
18
package.json
|
@ -3,23 +3,23 @@
|
||||||
"description" : "A Etherpad based on node.js",
|
"description" : "A Etherpad based on node.js",
|
||||||
"homepage" : "https://github.com/Pita/etherpad-lite",
|
"homepage" : "https://github.com/Pita/etherpad-lite",
|
||||||
"keywords" : ["etherpad", "realtime", "collaborative", "editor"],
|
"keywords" : ["etherpad", "realtime", "collaborative", "editor"],
|
||||||
"author" : "Peter 'Pita' Martischka <petermartischka@googlemail.com>",
|
"author" : "Peter 'Pita' Martischka <petermartischka@googlemail.com> - Primary Technology Ltd",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
{ "name": "John McLear",
|
{ "name": "John McLear",
|
||||||
"name": "Hans Pinckaers",
|
"name": "Hans Pinckaers",
|
||||||
"name": "Robin Buse"}
|
"name": "Robin Buse"}
|
||||||
],
|
],
|
||||||
"dependencies" : {
|
"dependencies" : {
|
||||||
"socket.io" : "0.7.9",
|
"socket.io" : "0.8.7",
|
||||||
"ueberDB" : "0.1.2",
|
"ueberDB" : "0.1.3",
|
||||||
"async" : "0.1.9",
|
"async" : "0.1.15",
|
||||||
"joose" : "3.18.0",
|
"joose" : "3.50.0",
|
||||||
"express" : "2.4.5",
|
"express" : "2.5.0",
|
||||||
"clean-css" : "0.2.4",
|
"clean-css" : "0.2.4",
|
||||||
"uglify-js" : "1.0.7",
|
"uglify-js" : "1.1.1",
|
||||||
"gzip" : "0.1.0",
|
"gzip" : "0.1.0",
|
||||||
"formidable" : "1.0.2",
|
"formidable" : "1.0.7",
|
||||||
"log4js" : "0.3.8"
|
"log4js" : "0.3.9"
|
||||||
},
|
},
|
||||||
"version" : "1.0.0"
|
"version" : "1.0.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,8 @@
|
||||||
"ip": "0.0.0.0",
|
"ip": "0.0.0.0",
|
||||||
"port" : 9001,
|
"port" : 9001,
|
||||||
|
|
||||||
//The Type of the database. You can choose between sqlite and mysql
|
//The Type of the database. You can choose between dirty, sqlite and mysql
|
||||||
|
//You should use mysql or sqlite for anything else than testing or development
|
||||||
"dbType" : "dirty",
|
"dbType" : "dirty",
|
||||||
//the database specific settings
|
//the database specific settings
|
||||||
"dbSettings" : {
|
"dbSettings" : {
|
||||||
|
|
|
@ -93,7 +93,7 @@ a img
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="file"] {
|
input[type="file"] {
|
||||||
color: #fff;
|
color: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
#editbar ul li.separator
|
#editbar ul li.separator
|
||||||
|
|
|
@ -86,12 +86,13 @@
|
||||||
input[type="submit"]::-moz-focus-inner { border: 0 }
|
input[type="submit"]::-moz-focus-inner { border: 0 }
|
||||||
@-moz-document url-prefix() { input[type="submit"] { padding: 7px } }
|
@-moz-document url-prefix() { input[type="submit"] { padding: 7px } }
|
||||||
</style>
|
</style>
|
||||||
|
<body onload="document.mainform.padname.focus();">
|
||||||
<link href="static/custom/index.css" rel="stylesheet">
|
<link href="static/custom/index.css" rel="stylesheet">
|
||||||
<script src="static/custom/index.js"></script>
|
<script src="static/custom/index.js"></script>
|
||||||
<div id="container">
|
<div id="container">
|
||||||
<div id="button" onclick="go2Random()">New Pad</div><br><div id="label">or create/open a Pad with the name</div>
|
<div id="button" onclick="go2Random()">New Pad</div><br><div id="label">or create/open a Pad with the name</div>
|
||||||
<form action="#" onsubmit="go2Name();return false;">
|
<form action="#" name="mainform" onsubmit="go2Name();return false;">
|
||||||
<input type="text" id="padname" autofocus>
|
<input type="text" name="padname" id="padname" autofocus>
|
||||||
<input type="submit" value="OK">
|
<input type="submit" value="OK">
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -123,4 +124,5 @@
|
||||||
//start the costum js
|
//start the costum js
|
||||||
if(typeof costumStart == "function") costumStart();
|
if(typeof costumStart == "function") costumStart();
|
||||||
</script>
|
</script>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -3097,7 +3097,11 @@ function OUTER(gscope)
|
||||||
// Such a div is what IE 6 creates naturally when you make a blank line
|
// Such a div is what IE 6 creates naturally when you make a blank line
|
||||||
// in a document of divs. However, when copy-and-pasted the div will
|
// in a document of divs. However, when copy-and-pasted the div will
|
||||||
// contain a space, so we note its emptiness with a property.
|
// contain a space, so we note its emptiness with a property.
|
||||||
|
if(browser.version < 9) {
|
||||||
lineElem.innerHTML = "";
|
lineElem.innerHTML = "";
|
||||||
|
} else {
|
||||||
|
lineElem.innerHTML = "<hr style=\"border:none; color:#fff; height:1px; display:none\"/>";
|
||||||
|
}
|
||||||
// a primitive-valued property survives copy-and-paste
|
// a primitive-valued property survives copy-and-paste
|
||||||
setAssoc(lineElem, "shouldBeEmpty", true);
|
setAssoc(lineElem, "shouldBeEmpty", true);
|
||||||
// an object property doesn't
|
// an object property doesn't
|
||||||
|
@ -3575,12 +3579,18 @@ function OUTER(gscope)
|
||||||
editorInfo.ace_doIndentOutdent = doIndentOutdent;
|
editorInfo.ace_doIndentOutdent = doIndentOutdent;
|
||||||
|
|
||||||
function doTabKey(shiftDown)
|
function doTabKey(shiftDown)
|
||||||
|
{
|
||||||
|
if (shiftDown === true){
|
||||||
|
doDeleteKey();
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
if (!doIndentOutdent(shiftDown))
|
if (!doIndentOutdent(shiftDown))
|
||||||
{
|
{
|
||||||
performDocumentReplaceSelection(THE_TAB);
|
performDocumentReplaceSelection(THE_TAB);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function doDeleteKey(optEvt)
|
function doDeleteKey(optEvt)
|
||||||
{
|
{
|
||||||
|
|
|
@ -152,6 +152,10 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument)
|
||||||
{
|
{
|
||||||
if (href)
|
if (href)
|
||||||
{
|
{
|
||||||
|
if(!~href.indexOf("http")) // if the url doesn't include http or https etc prefix it.
|
||||||
|
{
|
||||||
|
href = "http://"+href;
|
||||||
|
}
|
||||||
extraOpenTags = extraOpenTags + '<a href="' + href.replace(/\"/g, '"') + '">';
|
extraOpenTags = extraOpenTags + '<a href="' + href.replace(/\"/g, '"') + '">';
|
||||||
extraCloseTags = '</a>' + extraCloseTags;
|
extraCloseTags = '</a>' + extraCloseTags;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
var socket;
|
var socket;
|
||||||
var LineNumbersDisabled = false;
|
var LineNumbersDisabled = false;
|
||||||
|
var noColors = false;
|
||||||
var useMonospaceFontGlobal = false;
|
var useMonospaceFontGlobal = false;
|
||||||
var globalUserName = false;
|
var globalUserName = false;
|
||||||
|
|
||||||
|
@ -78,11 +79,21 @@ function randomString()
|
||||||
|
|
||||||
function getParams()
|
function getParams()
|
||||||
{
|
{
|
||||||
var showControls = getUrlVars()["showControls"];
|
var params = getUrlVars()
|
||||||
var showChat = getUrlVars()["showChat"];
|
var showControls = params["showControls"];
|
||||||
var userName = getUrlVars()["userName"];
|
var showChat = params["showChat"];
|
||||||
var showLineNumbers = getUrlVars()["showLineNumbers"];
|
var userName = params["userName"];
|
||||||
var useMonospaceFont = getUrlVars()["useMonospaceFont"];
|
var showLineNumbers = params["showLineNumbers"];
|
||||||
|
var useMonospaceFont = params["useMonospaceFont"];
|
||||||
|
var IsnoColors = params["noColors"];
|
||||||
|
|
||||||
|
if(IsnoColors)
|
||||||
|
{
|
||||||
|
if(IsnoColors == "true")
|
||||||
|
{
|
||||||
|
noColors = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
if(showControls)
|
if(showControls)
|
||||||
{
|
{
|
||||||
if(showControls == "false")
|
if(showControls == "false")
|
||||||
|
@ -120,7 +131,7 @@ function getParams()
|
||||||
if(userName)
|
if(userName)
|
||||||
{
|
{
|
||||||
// If the username is set as a parameter we should set a global value that we can call once we have initiated the pad.
|
// If the username is set as a parameter we should set a global value that we can call once we have initiated the pad.
|
||||||
globalUserName = userName;
|
globalUserName = unescape(userName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,6 +247,13 @@ function handshake()
|
||||||
{
|
{
|
||||||
pad.changeViewOption('showLineNumbers', false);
|
pad.changeViewOption('showLineNumbers', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the noColors value is set to true then we need to hide the backround colors on the ace spans
|
||||||
|
if (noColors == true)
|
||||||
|
{
|
||||||
|
pad.changeViewOption('noColors', true);
|
||||||
|
}
|
||||||
|
|
||||||
// If the Monospacefont value is set to true then change it to monospace.
|
// If the Monospacefont value is set to true then change it to monospace.
|
||||||
if (useMonospaceFontGlobal == true)
|
if (useMonospaceFontGlobal == true)
|
||||||
{
|
{
|
||||||
|
@ -245,6 +263,7 @@ function handshake()
|
||||||
if (globalUserName !== false)
|
if (globalUserName !== false)
|
||||||
{
|
{
|
||||||
pad.notifyChangeName(globalUserName); // Notifies the server
|
pad.notifyChangeName(globalUserName); // Notifies the server
|
||||||
|
pad.myUserInfo.name = globalUserName;
|
||||||
$('#myusernameedit').attr({"value":globalUserName}); // Updates the current users UI
|
$('#myusernameedit').attr({"value":globalUserName}); // Updates the current users UI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,8 @@ var padeditor = (function()
|
||||||
{
|
{
|
||||||
pad.changeViewOption('useMonospaceFont', $("#viewfontmenu").val() == 'monospace');
|
pad.changeViewOption('useMonospaceFont', $("#viewfontmenu").val() == 'monospace');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
noColors = !noColors; // Inversed so we can pass it to showauthorcolors
|
||||||
},
|
},
|
||||||
setViewOptions: function(newOptions)
|
setViewOptions: function(newOptions)
|
||||||
{
|
{
|
||||||
|
@ -84,6 +86,9 @@ var padeditor = (function()
|
||||||
v = getOption('useMonospaceFont', false);
|
v = getOption('useMonospaceFont', false);
|
||||||
self.ace.setProperty("textface", (v ? "monospace" : "Arial, sans-serif"));
|
self.ace.setProperty("textface", (v ? "monospace" : "Arial, sans-serif"));
|
||||||
$("#viewfontmenu").val(v ? "monospace" : "normal");
|
$("#viewfontmenu").val(v ? "monospace" : "normal");
|
||||||
|
|
||||||
|
self.ace.setProperty("showsauthorcolors", noColors);
|
||||||
|
|
||||||
},
|
},
|
||||||
initViewZoom: function()
|
initViewZoom: function()
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue