Merge branch 'develop' of git://github.com/Pita/etherpad-lite into develop
|
@ -1,7 +1,5 @@
|
||||||
node_modules
|
node_modules
|
||||||
settings.json
|
settings.json
|
||||||
static/js/jquery.js
|
|
||||||
static/js/prefixfree.js
|
|
||||||
APIKEY.txt
|
APIKEY.txt
|
||||||
bin/abiword.exe
|
bin/abiword.exe
|
||||||
bin/node.exe
|
bin/node.exe
|
||||||
|
@ -10,4 +8,7 @@ var/dirty.db
|
||||||
bin/convertSettings.json
|
bin/convertSettings.json
|
||||||
*~
|
*~
|
||||||
*.patch
|
*.patch
|
||||||
*.DS_Store
|
src/static/js/jquery.js
|
||||||
|
npm-debug.log
|
||||||
|
*.DS_Store
|
||||||
|
.ep_initialized
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
So, a plugin is an npm package whose name starts with ep_ and that contains a file ep.json
|
||||||
|
require("ep_etherpad-lite/static/js/plugingfw/plugins").update() will use npm to list all installed modules and read their ep.json files. These will contain registrations for hooks which are loaded
|
||||||
|
A hook registration is a pairs of a hook name and a function reference (filename for require() plus function name)
|
||||||
|
require("ep_etherpad-lite/static/js/plugingfw/hooks").callAll("hook_name", {argname:value}) will call all hook functions registered for hook_name
|
||||||
|
That is the basis.
|
||||||
|
Ok, so that was a slight simplification: inside ep.json, hook registrations are grouped into groups called "parts". Parts from all plugins are ordered using a topological sort according to "pre" and "post" pointers to other plugins/parts (just like dependencies, but non-installed plugins are silently ignored).
|
||||||
|
This ordering is honored when you do callAll(hook_name) - hook functions for that hook_name are called in that order
|
||||||
|
Ordering between plugins is undefined, only parts are ordered.
|
||||||
|
|
||||||
|
A plugin usually has one part, but it van have multiple.
|
||||||
|
This is so that it can insert some hook registration before that of another plugin, and another one after.
|
||||||
|
This is important for e.g. registering URL-handlers for the express webserver, if you have some very generic and some very specific url-regexps
|
||||||
|
So, that's basically it... apart from client-side hooks
|
||||||
|
which works the same way, but uses a separate member of the part (part.client_hooks vs part.hooks), and where the hook function must obviously reside in a file require():able from the client...
|
||||||
|
One thing more: The main etherpad tree is actually a plugin itself, called ep_etherpad-lite, and it has it's own ep.json...
|
||||||
|
was that clear?
|
|
@ -0,0 +1,7 @@
|
||||||
|
.git*
|
||||||
|
docs/
|
||||||
|
examples/
|
||||||
|
support/
|
||||||
|
test/
|
||||||
|
testing.js
|
||||||
|
.DS_Store
|
|
@ -0,0 +1,36 @@
|
||||||
|
{
|
||||||
|
"parts": [
|
||||||
|
{
|
||||||
|
"name": "somepart",
|
||||||
|
"pre": [],
|
||||||
|
"post": ["ep_onemoreplugin/partone"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "partlast",
|
||||||
|
"pre": ["ep_fintest/otherpart"],
|
||||||
|
"post": [],
|
||||||
|
"hooks": {
|
||||||
|
"somehookname": "ep_fintest/partlast:somehook"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "partfirst",
|
||||||
|
"pre": [],
|
||||||
|
"post": ["ep_onemoreplugin/somepart"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "otherpart",
|
||||||
|
"pre": ["ep_fintest/somepart", "ep_otherplugin/main"],
|
||||||
|
"post": [],
|
||||||
|
"hooks": {
|
||||||
|
"somehookname": "ep_fintest/otherpart:somehook",
|
||||||
|
"morehook": "ep_fintest/otherpart:morehook",
|
||||||
|
"expressCreateServer": "ep_fintest/otherpart:expressServer",
|
||||||
|
"eejsBlock_editbarMenuLeft": "ep_fintest/otherpart:eejsBlock_editbarMenuLeft"
|
||||||
|
},
|
||||||
|
"client_hooks": {
|
||||||
|
"somehookname": "ep_fintest/static/js/test:bar"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
test = require("ep_fintest/static/js/test.js");
|
||||||
|
console.log("FOOO:", test.foo);
|
||||||
|
|
||||||
|
exports.somehook = function (hook_name, args, cb) {
|
||||||
|
return cb(["otherpart:somehook was here"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.morehook = function (hook_name, args, cb) {
|
||||||
|
return cb(["otherpart:morehook was here"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.expressServer = function (hook_name, args, cb) {
|
||||||
|
args.app.get('/otherpart', function(req, res) {
|
||||||
|
res.send("<em>Abra cadabra</em>");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.eejsBlock_editbarMenuLeft = function (hook_name, args, cb) {
|
||||||
|
args.content = args.content + '\
|
||||||
|
<li id="testButton" onClick="window.pad&&pad.editbarClick(\'clearauthorship\');return false;">\
|
||||||
|
<a class="buttonicon buttonicon-test" title="Test test test"></a>\
|
||||||
|
</li>\
|
||||||
|
';
|
||||||
|
return cb();
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"name": "ep_fintest",
|
||||||
|
"description": "A test plugin",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"author": "RedHog (Egil Moeller) <egil.moller@freecode.no>",
|
||||||
|
"contributors": [],
|
||||||
|
"dependencies": {},
|
||||||
|
"engines": { "node": ">= 0.4.1 < 0.7.0" }
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
exports.somehook = function (hook_name, args, cb) {
|
||||||
|
return cb(["partlast:somehook was here"]);
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
exports.foo = 42;
|
||||||
|
|
||||||
|
exports.bar = function (hook_name, args, cb) {
|
||||||
|
return cb(["FOOOO"]);
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
<em>Test bla bla</em>
|
|
@ -15,8 +15,8 @@ var log4js = require("log4js");
|
||||||
log4js.setGlobalLogLevel("INFO");
|
log4js.setGlobalLogLevel("INFO");
|
||||||
var async = require("async");
|
var async = require("async");
|
||||||
var db = require('../node/db/DB');
|
var db = require('../node/db/DB');
|
||||||
var CommonCode = require('../node/utils/common_code');
|
|
||||||
var Changeset = CommonCode.require("/Changeset");
|
var Changeset = require("ep_etherpad-lite/static/js/Changeset");
|
||||||
var padManager;
|
var padManager;
|
||||||
|
|
||||||
async.series([
|
async.series([
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
var CommonCode = require('../node/utils/common_code');
|
|
||||||
var startTime = new Date().getTime();
|
var startTime = new Date().getTime();
|
||||||
var fs = require("fs");
|
var fs = require("fs");
|
||||||
var ueberDB = require("ueberDB");
|
var ueberDB = require("ueberDB");
|
||||||
var mysql = require("mysql");
|
var mysql = require("mysql");
|
||||||
var async = require("async");
|
var async = require("async");
|
||||||
var Changeset = CommonCode.require("/Changeset");
|
var Changeset = require("ep_etherpad-lite/static/js/Changeset");
|
||||||
var randomString = CommonCode.require('/pad_utils').randomString;
|
var randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString;
|
||||||
var AttributePoolFactory = CommonCode.require("/AttributePoolFactory");
|
var AttributePool = require("ep_etherpad-lite/static/js/AttributePool");
|
||||||
|
|
||||||
var settingsFile = process.argv[2];
|
var settingsFile = process.argv[2];
|
||||||
var sqlOutputFile = process.argv[3];
|
var sqlOutputFile = process.argv[3];
|
||||||
|
@ -384,7 +384,7 @@ function convertPad(padId, callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
//generate the latest atext
|
//generate the latest atext
|
||||||
var fullAPool = AttributePoolFactory.createAttributePool().fromJsonable(apool);
|
var fullAPool = (new AttributePool()).fromJsonable(apool);
|
||||||
var keyRev = Math.floor(padmeta.head / padmeta.keyRevInterval) * padmeta.keyRevInterval;
|
var keyRev = Math.floor(padmeta.head / padmeta.keyRevInterval) * padmeta.keyRevInterval;
|
||||||
var atext = changesetsMeta[keyRev].atext;
|
var atext = changesetsMeta[keyRev].atext;
|
||||||
var curRev = keyRev;
|
var curRev = keyRev;
|
||||||
|
|
|
@ -22,8 +22,7 @@ node-inspector &
|
||||||
|
|
||||||
echo "If you are new to node-inspector, take a look at this video: http://youtu.be/AOnK3NVnxL8"
|
echo "If you are new to node-inspector, take a look at this video: http://youtu.be/AOnK3NVnxL8"
|
||||||
|
|
||||||
cd "node"
|
node --debug node_modules/ep_etherpad-lite/node/server.js $*
|
||||||
node --debug server.js
|
|
||||||
|
|
||||||
#kill node-inspector before ending
|
#kill node-inspector before ending
|
||||||
kill $!
|
kill $!
|
||||||
|
|
|
@ -55,7 +55,13 @@ if [ ! -f $settings ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Ensure that all dependencies are up to date..."
|
echo "Ensure that all dependencies are up to date..."
|
||||||
npm install || {
|
(
|
||||||
|
mkdir -p node_modules
|
||||||
|
cd node_modules
|
||||||
|
[ -e ep_etherpad-lite ] || ln -s ../src ep_etherpad-lite
|
||||||
|
cd ep_etherpad-lite
|
||||||
|
npm install
|
||||||
|
) || {
|
||||||
rm -rf node_modules
|
rm -rf node_modules
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
@ -63,8 +69,8 @@ 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.7.1"
|
NEEDED_VERSION="1.7.1"
|
||||||
if [ -f "static/js/jquery.js" ]; then
|
if [ -f "src/static/js/jquery.js" ]; then
|
||||||
VERSION=$(cat static/js/jquery.js | head -n 3 | grep -o "v[0-9]\.[0-9]\(\.[0-9]\)\?");
|
VERSION=$(cat src/static/js/jquery.js | head -n 3 | grep -o "v[0-9]\.[0-9]\(\.[0-9]\)\?");
|
||||||
|
|
||||||
if [ ${VERSION#v} = $NEEDED_VERSION ]; then
|
if [ ${VERSION#v} = $NEEDED_VERSION ]; then
|
||||||
DOWNLOAD_JQUERY="false"
|
DOWNLOAD_JQUERY="false"
|
||||||
|
@ -72,22 +78,7 @@ if [ -f "static/js/jquery.js" ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $DOWNLOAD_JQUERY = "true" ]; then
|
if [ $DOWNLOAD_JQUERY = "true" ]; then
|
||||||
curl -lo static/js/jquery.js http://code.jquery.com/jquery-$NEEDED_VERSION.js || exit 1
|
curl -lo src/static/js/jquery.js http://code.jquery.com/jquery-$NEEDED_VERSION.js || exit 1
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Ensure prefixfree is downloaded and up to date..."
|
|
||||||
DOWNLOAD_PREFIXFREE="true"
|
|
||||||
NEEDED_VERSION="1.0.4"
|
|
||||||
if [ -f "static/js/prefixfree.js" ]; then
|
|
||||||
VERSION=$(cat static/js/prefixfree.js | grep "PrefixFree" | grep -o "[0-9].[0-9].[0-9]");
|
|
||||||
|
|
||||||
if [ $VERSION = $NEEDED_VERSION ]; then
|
|
||||||
DOWNLOAD_PREFIXFREE="false"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ $DOWNLOAD_PREFIXFREE = "true" ]; then
|
|
||||||
curl -lo static/js/prefixfree.js -k https://raw.github.com/LeaVerou/prefixfree/master/prefixfree.js || exit 1
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
#Remove all minified data to force node creating it new
|
#Remove all minified data to force node creating it new
|
||||||
|
@ -98,12 +89,12 @@ echo "ensure custom css/js files are created..."
|
||||||
|
|
||||||
for f in "index" "pad" "timeslider"
|
for f in "index" "pad" "timeslider"
|
||||||
do
|
do
|
||||||
if [ ! -f "static/custom/$f.js" ]; then
|
if [ ! -f "src/static/custom/$f.js" ]; then
|
||||||
cp -v "static/custom/js.template" "static/custom/$f.js" || exit 1
|
cp -v "src/static/custom/js.template" "src/static/custom/$f.js" || exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -f "static/custom/$f.css" ]; then
|
if [ ! -f "src/static/custom/$f.css" ]; then
|
||||||
cp -v "static/custom/css.template" "static/custom/$f.css" || exit 1
|
cp -v "src/static/custom/css.template" "src/static/custom/$f.css" || exit 1
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|
|
@ -25,5 +25,4 @@ bin/installDeps.sh $* || exit 1
|
||||||
|
|
||||||
#Move to the node folder and start
|
#Move to the node folder and start
|
||||||
echo "start..."
|
echo "start..."
|
||||||
cd "node"
|
node node_modules/ep_etherpad-lite/node/server.js $*
|
||||||
node server.js $*
|
|
||||||
|
|
500
node/server.js
|
@ -1,500 +0,0 @@
|
||||||
/**
|
|
||||||
* This module is started with bin/run.sh. It sets up a Express HTTP and a Socket.IO Server.
|
|
||||||
* Static file Requests are answered directly from this module, Socket.IO messages are passed
|
|
||||||
* to MessageHandler and minfied requests are passed to minified.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 2011 Peter 'Pita' Martischka (Primary Technology Ltd)
|
|
||||||
*
|
|
||||||
* 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 ERR = require("async-stacktrace");
|
|
||||||
var log4js = require('log4js');
|
|
||||||
var os = require("os");
|
|
||||||
var socketio = require('socket.io');
|
|
||||||
var fs = require('fs');
|
|
||||||
var settings = require('./utils/Settings');
|
|
||||||
var db = require('./db/DB');
|
|
||||||
var async = require('async');
|
|
||||||
var express = require('express');
|
|
||||||
var path = require('path');
|
|
||||||
var minify = require('./utils/Minify');
|
|
||||||
var CachingMiddleware = require('./utils/caching_middleware');
|
|
||||||
var Yajsml = require('yajsml');
|
|
||||||
var formidable = require('formidable');
|
|
||||||
var apiHandler;
|
|
||||||
var exportHandler;
|
|
||||||
var importHandler;
|
|
||||||
var exporthtml;
|
|
||||||
var readOnlyManager;
|
|
||||||
var padManager;
|
|
||||||
var securityManager;
|
|
||||||
var socketIORouter;
|
|
||||||
|
|
||||||
//try to get the git version
|
|
||||||
var version = "";
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var rootPath = path.normalize(__dirname + "/../")
|
|
||||||
var ref = fs.readFileSync(rootPath + ".git/HEAD", "utf-8");
|
|
||||||
var refPath = rootPath + ".git/" + ref.substring(5, ref.indexOf("\n"));
|
|
||||||
version = fs.readFileSync(refPath, "utf-8");
|
|
||||||
version = version.substring(0, 7);
|
|
||||||
console.log("Your Etherpad Lite git version is " + version);
|
|
||||||
}
|
|
||||||
catch(e)
|
|
||||||
{
|
|
||||||
console.warn("Can't get git version for server header\n" + e.message)
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("Report bugs at https://github.com/Pita/etherpad-lite/issues")
|
|
||||||
|
|
||||||
var serverName = "Etherpad-Lite " + version + " (http://j.mp/ep-lite)";
|
|
||||||
|
|
||||||
exports.maxAge = settings.maxAge;
|
|
||||||
|
|
||||||
//set loglevel
|
|
||||||
log4js.setGlobalLogLevel(settings.loglevel);
|
|
||||||
|
|
||||||
async.waterfall([
|
|
||||||
//initalize the database
|
|
||||||
function (callback)
|
|
||||||
{
|
|
||||||
db.init(callback);
|
|
||||||
},
|
|
||||||
//initalize the http server
|
|
||||||
function (callback)
|
|
||||||
{
|
|
||||||
//create server
|
|
||||||
var app = express.createServer();
|
|
||||||
|
|
||||||
app.use(function (req, res, next) {
|
|
||||||
res.header("Server", serverName);
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
//redirects browser to the pad's sanitized url if needed. otherwise, renders the html
|
|
||||||
app.param('pad', function (req, res, next, padId) {
|
|
||||||
//ensure the padname is valid and the url doesn't end with a /
|
|
||||||
if(!padManager.isValidPadId(padId) || /\/$/.test(req.url))
|
|
||||||
{
|
|
||||||
res.send('Such a padname is forbidden', 404);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
padManager.sanitizePadId(padId, function(sanitizedPadId) {
|
|
||||||
//the pad id was sanitized, so we redirect to the sanitized version
|
|
||||||
if(sanitizedPadId != padId)
|
|
||||||
{
|
|
||||||
var real_path = req.path.replace(/^\/p\/[^\/]+/, './' + sanitizedPadId);
|
|
||||||
res.header('Location', real_path);
|
|
||||||
res.send('You should be redirected to <a href="' + real_path + '">' + real_path + '</a>', 302);
|
|
||||||
}
|
|
||||||
//the pad id was fine, so just render it
|
|
||||||
else
|
|
||||||
{
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//load modules that needs a initalized db
|
|
||||||
readOnlyManager = require("./db/ReadOnlyManager");
|
|
||||||
exporthtml = require("./utils/ExportHtml");
|
|
||||||
exportHandler = require('./handler/ExportHandler');
|
|
||||||
importHandler = require('./handler/ImportHandler');
|
|
||||||
apiHandler = require('./handler/APIHandler');
|
|
||||||
padManager = require('./db/PadManager');
|
|
||||||
securityManager = require('./db/SecurityManager');
|
|
||||||
socketIORouter = require("./handler/SocketIORouter");
|
|
||||||
|
|
||||||
//install logging
|
|
||||||
var httpLogger = log4js.getLogger("http");
|
|
||||||
app.configure(function()
|
|
||||||
{
|
|
||||||
// Activate http basic auth if it has been defined in settings.json
|
|
||||||
if(settings.httpAuth != null) app.use(basic_auth);
|
|
||||||
|
|
||||||
// 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(express.cookieParser());
|
|
||||||
});
|
|
||||||
|
|
||||||
app.error(function(err, req, res, next){
|
|
||||||
res.send(500);
|
|
||||||
console.error(err.stack ? err.stack : err.toString());
|
|
||||||
gracefulShutdown();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Cache both minified and static.
|
|
||||||
var assetCache = new CachingMiddleware;
|
|
||||||
app.all('/(minified|static)/*', assetCache.handle);
|
|
||||||
|
|
||||||
// Minify will serve static files compressed (minify enabled). It also has
|
|
||||||
// file-specific hacks for ace/require-kernel/etc.
|
|
||||||
app.all('/static/:filename(*)', minify.minify);
|
|
||||||
|
|
||||||
// Setup middleware that will package JavaScript files served by minify for
|
|
||||||
// CommonJS loader on the client-side.
|
|
||||||
var jsServer = new (Yajsml.Server)({
|
|
||||||
rootPath: 'minified/'
|
|
||||||
, rootURI: 'http://localhost:' + settings.port + '/static/js/'
|
|
||||||
});
|
|
||||||
var StaticAssociator = Yajsml.associators.StaticAssociator;
|
|
||||||
var associations =
|
|
||||||
Yajsml.associators.associationsForSimpleMapping(minify.tar);
|
|
||||||
var associator = new StaticAssociator(associations);
|
|
||||||
jsServer.setAssociator(associator);
|
|
||||||
app.use(jsServer);
|
|
||||||
|
|
||||||
//checks for padAccess
|
|
||||||
function hasPadAccess(req, res, callback)
|
|
||||||
{
|
|
||||||
securityManager.checkAccess(req.params.pad, req.cookies.sessionid, req.cookies.token, req.cookies.password, function(err, accessObj)
|
|
||||||
{
|
|
||||||
if(ERR(err, callback)) return;
|
|
||||||
|
|
||||||
//there is access, continue
|
|
||||||
if(accessObj.accessStatus == "grant")
|
|
||||||
{
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
//no access
|
|
||||||
else
|
|
||||||
{
|
|
||||||
res.send("403 - Can't touch this", 403);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//checks for basic http auth
|
|
||||||
function basic_auth (req, res, next) {
|
|
||||||
if (req.headers.authorization && req.headers.authorization.search('Basic ') === 0) {
|
|
||||||
// fetch login and password
|
|
||||||
if (new Buffer(req.headers.authorization.split(' ')[1], 'base64').toString() == settings.httpAuth) {
|
|
||||||
next();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res.header('WWW-Authenticate', 'Basic realm="Protected Area"');
|
|
||||||
if (req.headers.authorization) {
|
|
||||||
setTimeout(function () {
|
|
||||||
res.send('Authentication required', 401);
|
|
||||||
}, 1000);
|
|
||||||
} else {
|
|
||||||
res.send('Authentication required', 401);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//serve read only pad
|
|
||||||
app.get('/ro/:id', function(req, res)
|
|
||||||
{
|
|
||||||
var html;
|
|
||||||
var padId;
|
|
||||||
var pad;
|
|
||||||
|
|
||||||
async.series([
|
|
||||||
//translate the read only pad to a padId
|
|
||||||
function(callback)
|
|
||||||
{
|
|
||||||
readOnlyManager.getPadId(req.params.id, function(err, _padId)
|
|
||||||
{
|
|
||||||
if(ERR(err, callback)) return;
|
|
||||||
|
|
||||||
padId = _padId;
|
|
||||||
|
|
||||||
//we need that to tell hasPadAcess about the pad
|
|
||||||
req.params.pad = padId;
|
|
||||||
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
//render the html document
|
|
||||||
function(callback)
|
|
||||||
{
|
|
||||||
//return if the there is no padId
|
|
||||||
if(padId == null)
|
|
||||||
{
|
|
||||||
callback("notfound");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
hasPadAccess(req, res, function()
|
|
||||||
{
|
|
||||||
//render the html document
|
|
||||||
exporthtml.getPadHTMLDocument(padId, null, false, function(err, _html)
|
|
||||||
{
|
|
||||||
if(ERR(err, callback)) return;
|
|
||||||
html = _html;
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
], function(err)
|
|
||||||
{
|
|
||||||
//throw any unexpected error
|
|
||||||
if(err && err != "notfound")
|
|
||||||
ERR(err);
|
|
||||||
|
|
||||||
if(err == "notfound")
|
|
||||||
res.send('404 - Not Found', 404);
|
|
||||||
else
|
|
||||||
res.send(html);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
//serve pad.html under /p
|
|
||||||
app.get('/p/:pad', function(req, res, next)
|
|
||||||
{
|
|
||||||
var filePath = path.normalize(__dirname + "/../static/pad.html");
|
|
||||||
res.sendfile(filePath, { maxAge: exports.maxAge });
|
|
||||||
});
|
|
||||||
|
|
||||||
//serve timeslider.html under /p/$padname/timeslider
|
|
||||||
app.get('/p/:pad/timeslider', function(req, res, next)
|
|
||||||
{
|
|
||||||
var filePath = path.normalize(__dirname + "/../static/timeslider.html");
|
|
||||||
res.sendfile(filePath, { maxAge: exports.maxAge });
|
|
||||||
});
|
|
||||||
|
|
||||||
//serve timeslider.html under /p/$padname/timeslider
|
|
||||||
app.get('/p/:pad/:rev?/export/:type', function(req, res, next)
|
|
||||||
{
|
|
||||||
var types = ["pdf", "doc", "txt", "html", "odt", "dokuwiki"];
|
|
||||||
//send a 404 if we don't support this filetype
|
|
||||||
if(types.indexOf(req.params.type) == -1)
|
|
||||||
{
|
|
||||||
next();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//if abiword is disabled, and this is a format we only support with abiword, output a message
|
|
||||||
if(settings.abiword == null &&
|
|
||||||
["odt", "pdf", "doc"].indexOf(req.params.type) !== -1)
|
|
||||||
{
|
|
||||||
res.send("Abiword is not enabled at this Etherpad Lite instance. Set the path to Abiword in settings.json to enable this feature");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
res.header("Access-Control-Allow-Origin", "*");
|
|
||||||
|
|
||||||
hasPadAccess(req, res, function()
|
|
||||||
{
|
|
||||||
exportHandler.doExport(req, res, req.params.pad, req.params.type);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
//handle import requests
|
|
||||||
app.post('/p/:pad/import', function(req, res, next)
|
|
||||||
{
|
|
||||||
//if abiword is disabled, skip handling this request
|
|
||||||
if(settings.abiword == null)
|
|
||||||
{
|
|
||||||
next();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
hasPadAccess(req, res, function()
|
|
||||||
{
|
|
||||||
importHandler.doImport(req, res, req.params.pad);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
var apiLogger = log4js.getLogger("API");
|
|
||||||
|
|
||||||
//This is for making an api call, collecting all post information and passing it to the apiHandler
|
|
||||||
var apiCaller = function(req, res, fields)
|
|
||||||
{
|
|
||||||
res.header("Content-Type", "application/json; charset=utf-8");
|
|
||||||
|
|
||||||
apiLogger.info("REQUEST, " + req.params.func + ", " + JSON.stringify(fields));
|
|
||||||
|
|
||||||
//wrap the send function so we can log the response
|
|
||||||
res._send = res.send;
|
|
||||||
res.send = function(response)
|
|
||||||
{
|
|
||||||
response = JSON.stringify(response);
|
|
||||||
apiLogger.info("RESPONSE, " + req.params.func + ", " + response);
|
|
||||||
|
|
||||||
//is this a jsonp call, if yes, add the function call
|
|
||||||
if(req.query.jsonp)
|
|
||||||
response = req.query.jsonp + "(" + response + ")";
|
|
||||||
|
|
||||||
res._send(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
//call the api handler
|
|
||||||
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
|
|
||||||
app.post('/ep/pad/connection-diagnostic-info', function(req, res)
|
|
||||||
{
|
|
||||||
new formidable.IncomingForm().parse(req, function(err, fields, files)
|
|
||||||
{
|
|
||||||
console.log("DIAGNOSTIC-INFO: " + fields.diagnosticInfo);
|
|
||||||
res.end("OK");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
//The Etherpad client side sends information about client side javscript errors
|
|
||||||
app.post('/jserror', function(req, res)
|
|
||||||
{
|
|
||||||
new formidable.IncomingForm().parse(req, function(err, fields, files)
|
|
||||||
{
|
|
||||||
console.error("CLIENT SIDE JAVASCRIPT ERROR: " + fields.errorInfo);
|
|
||||||
res.end("OK");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
//serve index.html under /
|
|
||||||
app.get('/', function(req, res)
|
|
||||||
{
|
|
||||||
var filePath = path.normalize(__dirname + "/../static/index.html");
|
|
||||||
res.sendfile(filePath, { maxAge: exports.maxAge });
|
|
||||||
});
|
|
||||||
|
|
||||||
//serve robots.txt
|
|
||||||
app.get('/robots.txt', function(req, res)
|
|
||||||
{
|
|
||||||
var filePath = path.normalize(__dirname + "/../static/robots.txt");
|
|
||||||
res.sendfile(filePath, { maxAge: exports.maxAge });
|
|
||||||
});
|
|
||||||
|
|
||||||
//serve favicon.ico
|
|
||||||
app.get('/favicon.ico', function(req, res)
|
|
||||||
{
|
|
||||||
var filePath = path.normalize(__dirname + "/../static/custom/favicon.ico");
|
|
||||||
res.sendfile(filePath, { maxAge: exports.maxAge }, function(err)
|
|
||||||
{
|
|
||||||
//there is no custom favicon, send the default favicon
|
|
||||||
if(err)
|
|
||||||
{
|
|
||||||
filePath = path.normalize(__dirname + "/../static/favicon.ico");
|
|
||||||
res.sendfile(filePath, { maxAge: exports.maxAge });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
//let the server listen
|
|
||||||
app.listen(settings.port, settings.ip);
|
|
||||||
console.log("Server is listening at " + settings.ip + ":" + settings.port);
|
|
||||||
|
|
||||||
var onShutdown = false;
|
|
||||||
var gracefulShutdown = function(err)
|
|
||||||
{
|
|
||||||
if(err && err.stack)
|
|
||||||
{
|
|
||||||
console.error(err.stack);
|
|
||||||
}
|
|
||||||
else if(err)
|
|
||||||
{
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
//ensure there is only one graceful shutdown running
|
|
||||||
if(onShutdown) return;
|
|
||||||
onShutdown = true;
|
|
||||||
|
|
||||||
console.log("graceful shutdown...");
|
|
||||||
|
|
||||||
//stop the http server
|
|
||||||
app.close();
|
|
||||||
|
|
||||||
//do the db shutdown
|
|
||||||
db.db.doShutdown(function()
|
|
||||||
{
|
|
||||||
console.log("db sucessfully closed.");
|
|
||||||
|
|
||||||
process.exit(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
setTimeout(function(){
|
|
||||||
process.exit(1);
|
|
||||||
}, 3000);
|
|
||||||
}
|
|
||||||
|
|
||||||
//connect graceful shutdown with sigint and uncaughtexception
|
|
||||||
if(os.type().indexOf("Windows") == -1)
|
|
||||||
{
|
|
||||||
//sigint is so far not working on windows
|
|
||||||
//https://github.com/joyent/node/issues/1553
|
|
||||||
process.on('SIGINT', gracefulShutdown);
|
|
||||||
}
|
|
||||||
|
|
||||||
process.on('uncaughtException', gracefulShutdown);
|
|
||||||
|
|
||||||
//init socket.io and redirect all requests to the MessageHandler
|
|
||||||
var io = socketio.listen(app);
|
|
||||||
|
|
||||||
//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
|
|
||||||
io.set('transports', ['xhr-polling']);
|
|
||||||
|
|
||||||
var socketIOLogger = log4js.getLogger("socket.io");
|
|
||||||
io.set('logger', {
|
|
||||||
debug: function (str)
|
|
||||||
{
|
|
||||||
socketIOLogger.debug.apply(socketIOLogger, arguments);
|
|
||||||
},
|
|
||||||
info: function (str)
|
|
||||||
{
|
|
||||||
socketIOLogger.info.apply(socketIOLogger, arguments);
|
|
||||||
},
|
|
||||||
warn: function (str)
|
|
||||||
{
|
|
||||||
socketIOLogger.warn.apply(socketIOLogger, arguments);
|
|
||||||
},
|
|
||||||
error: function (str)
|
|
||||||
{
|
|
||||||
socketIOLogger.error.apply(socketIOLogger, arguments);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
//minify socket.io javascript
|
|
||||||
if(settings.minify)
|
|
||||||
io.enable('browser client minification');
|
|
||||||
|
|
||||||
var padMessageHandler = require("./handler/PadMessageHandler");
|
|
||||||
var timesliderMessageHandler = require("./handler/TimesliderMessageHandler");
|
|
||||||
|
|
||||||
//Initalize the Socket.IO Router
|
|
||||||
socketIORouter.setSocketIO(io);
|
|
||||||
socketIORouter.addComponent("pad", padMessageHandler);
|
|
||||||
socketIORouter.addComponent("timeslider", timesliderMessageHandler);
|
|
||||||
|
|
||||||
callback(null);
|
|
||||||
}
|
|
||||||
]);
|
|
|
@ -13,7 +13,7 @@
|
||||||
"dbType" : "dirty",
|
"dbType" : "dirty",
|
||||||
//the database specific settings
|
//the database specific settings
|
||||||
"dbSettings" : {
|
"dbSettings" : {
|
||||||
"filename" : "../var/dirty.db"
|
"filename" : "var/dirty.db"
|
||||||
},
|
},
|
||||||
|
|
||||||
/* An Example of MySQL Configuration
|
/* An Example of MySQL Configuration
|
||||||
|
@ -39,9 +39,9 @@
|
||||||
but makes it impossible to debug the javascript/css */
|
but makes it impossible to debug the javascript/css */
|
||||||
"minify" : true,
|
"minify" : true,
|
||||||
|
|
||||||
/* How long may clients use served javascript code? Without versioning this
|
/* How long may clients use served javascript code (in seconds)? Without versioning this
|
||||||
is may cause problems during deployment. */
|
is may cause problems during deployment. Set to 0 to disable caching */
|
||||||
"maxAge" : 21600000, // 6 hours
|
"maxAge" : 21600, // 6 hours
|
||||||
|
|
||||||
/* This is the path to the Abiword executable. Setting it to null, disables abiword.
|
/* This is the path to the Abiword executable. Setting it to null, disables abiword.
|
||||||
Abiword is needed to enable the import/export of pads*/
|
Abiword is needed to enable the import/export of pads*/
|
||||||
|
@ -50,6 +50,12 @@
|
||||||
/* This setting is used if you need http basic auth */
|
/* This setting is used if you need http basic auth */
|
||||||
// "httpAuth" : "user:pass",
|
// "httpAuth" : "user:pass",
|
||||||
|
|
||||||
|
/* This setting is used for http basic auth for admin pages. If not set, the admin page won't be accessible from web*/
|
||||||
|
// "adminHttpAuth" : "user:pass",
|
||||||
|
|
||||||
/* The log level we are using, can be: DEBUG, INFO, WARN, ERROR */
|
/* The log level we are using, can be: DEBUG, INFO, WARN, ERROR */
|
||||||
"loglevel": "INFO"
|
"loglevel": "INFO",
|
||||||
|
|
||||||
|
/* cache 6 hours = 1000*60*60*6 */
|
||||||
|
"maxAge": 21600000
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
"dbType" : "dirty",
|
"dbType" : "dirty",
|
||||||
//the database specific settings
|
//the database specific settings
|
||||||
"dbSettings" : {
|
"dbSettings" : {
|
||||||
"filename" : "../var/dirty.db"
|
"filename" : "var/dirty.db"
|
||||||
},
|
},
|
||||||
|
|
||||||
/* An Example of MySQL Configuration
|
/* An Example of MySQL Configuration
|
||||||
|
@ -40,5 +40,9 @@
|
||||||
|
|
||||||
/* This is the path to the Abiword executable. Setting it to null, disables abiword.
|
/* This is the path to the Abiword executable. Setting it to null, disables abiword.
|
||||||
Abiword is needed to enable the import/export of pads*/
|
Abiword is needed to enable the import/export of pads*/
|
||||||
"abiword" : null
|
"abiword" : null,
|
||||||
|
|
||||||
|
/* cache 6 hours = 1000*60*60*6 */
|
||||||
|
"maxAge": 21600000
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"parts": [
|
||||||
|
{ "name": "static", "hooks": { "expressCreateServer": "ep_etherpad-lite/node/hooks/express/static:expressCreateServer" } },
|
||||||
|
{ "name": "specialpages", "hooks": { "expressCreateServer": "ep_etherpad-lite/node/hooks/express/specialpages:expressCreateServer" } },
|
||||||
|
{ "name": "padurlsanitize", "hooks": { "expressCreateServer": "ep_etherpad-lite/node/hooks/express/padurlsanitize:expressCreateServer" } },
|
||||||
|
{ "name": "padreadonly", "hooks": { "expressCreateServer": "ep_etherpad-lite/node/hooks/express/padreadonly:expressCreateServer" } },
|
||||||
|
{ "name": "webaccess", "hooks": { "expressConfigure": "ep_etherpad-lite/node/hooks/express/webaccess:expressConfigure" } },
|
||||||
|
{ "name": "apicalls", "hooks": { "expressCreateServer": "ep_etherpad-lite/node/hooks/express/apicalls:expressCreateServer" } },
|
||||||
|
{ "name": "importexport", "hooks": { "expressCreateServer": "ep_etherpad-lite/node/hooks/express/importexport:expressCreateServer" } },
|
||||||
|
{ "name": "errorhandling", "hooks": { "expressCreateServer": "ep_etherpad-lite/node/hooks/express/errorhandling:expressCreateServer" } },
|
||||||
|
{ "name": "socketio", "hooks": { "expressCreateServer": "ep_etherpad-lite/node/hooks/express/socketio:expressCreateServer" } },
|
||||||
|
{ "name": "adminplugins", "hooks": {
|
||||||
|
"expressCreateServer": "ep_etherpad-lite/node/hooks/express/adminplugins:expressCreateServer",
|
||||||
|
"socketio": "ep_etherpad-lite/node/hooks/express/adminplugins:socketio" } }
|
||||||
|
]
|
||||||
|
}
|
|
@ -18,11 +18,11 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var CommonCode = require('../utils/common_code');
|
|
||||||
var ERR = require("async-stacktrace");
|
var ERR = require("async-stacktrace");
|
||||||
var db = require("./DB").db;
|
var db = require("./DB").db;
|
||||||
var async = require("async");
|
var async = require("async");
|
||||||
var randomString = CommonCode.require('/pad_utils').randomString;
|
var randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the author exists
|
* Checks if the author exists
|
|
@ -18,10 +18,10 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var CommonCode = require('../utils/common_code');
|
|
||||||
var ERR = require("async-stacktrace");
|
var ERR = require("async-stacktrace");
|
||||||
var customError = require("../utils/customError");
|
var customError = require("../utils/customError");
|
||||||
var randomString = CommonCode.require('/pad_utils').randomString;
|
var randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString;
|
||||||
var db = require("./DB").db;
|
var db = require("./DB").db;
|
||||||
var async = require("async");
|
var async = require("async");
|
||||||
var padManager = require("./PadManager");
|
var padManager = require("./PadManager");
|
|
@ -2,11 +2,11 @@
|
||||||
* The pad object, defined with joose
|
* The pad object, defined with joose
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var CommonCode = require('../utils/common_code');
|
|
||||||
var ERR = require("async-stacktrace");
|
var ERR = require("async-stacktrace");
|
||||||
var Changeset = CommonCode.require("/Changeset");
|
var Changeset = require("ep_etherpad-lite/static/js/Changeset");
|
||||||
var AttributePoolFactory = CommonCode.require("/AttributePoolFactory");
|
var AttributePool = require("ep_etherpad-lite/static/js/AttributePool");
|
||||||
var randomString = CommonCode.require('/pad_utils').randomString;
|
var randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString;
|
||||||
var db = require("./DB").db;
|
var db = require("./DB").db;
|
||||||
var async = require("async");
|
var async = require("async");
|
||||||
var settings = require('../utils/Settings');
|
var settings = require('../utils/Settings');
|
||||||
|
@ -15,6 +15,11 @@ var padManager = require("./PadManager");
|
||||||
var padMessageHandler = require("../handler/PadMessageHandler");
|
var padMessageHandler = require("../handler/PadMessageHandler");
|
||||||
var readOnlyManager = require("./ReadOnlyManager");
|
var readOnlyManager = require("./ReadOnlyManager");
|
||||||
var crypto = require("crypto");
|
var crypto = require("crypto");
|
||||||
|
var randomString = require("../utils/randomstring");
|
||||||
|
|
||||||
|
//serialization/deserialization attributes
|
||||||
|
var attributeBlackList = ["id"];
|
||||||
|
var jsonableList = ["pool"];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copied from the Etherpad source code. It converts Windows line breaks to Unix line breaks and convert Tabs to spaces
|
* Copied from the Etherpad source code. It converts Windows line breaks to Unix line breaks and convert Tabs to spaces
|
||||||
|
@ -28,13 +33,13 @@ exports.cleanText = function (txt) {
|
||||||
var Pad = function Pad(id) {
|
var Pad = function Pad(id) {
|
||||||
|
|
||||||
this.atext = Changeset.makeAText("\n");
|
this.atext = Changeset.makeAText("\n");
|
||||||
this.pool = AttributePoolFactory.createAttributePool();
|
this.pool = new AttributePool();
|
||||||
this.head = -1;
|
this.head = -1;
|
||||||
this.chatHead = -1;
|
this.chatHead = -1;
|
||||||
this.publicStatus = false;
|
this.publicStatus = false;
|
||||||
this.passwordHash = null;
|
this.passwordHash = null;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
|
this.savedRevisions = [];
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.Pad = Pad;
|
exports.Pad = Pad;
|
||||||
|
@ -75,15 +80,28 @@ Pad.prototype.appendRevision = function appendRevision(aChangeset, author) {
|
||||||
newRevData.meta.atext = this.atext;
|
newRevData.meta.atext = this.atext;
|
||||||
}
|
}
|
||||||
|
|
||||||
db.set("pad:"+this.id+":revs:"+newRev, newRevData);
|
db.set("pad:"+this.id+":revs:"+newRev, newRevData);
|
||||||
db.set("pad:"+this.id, {atext: this.atext,
|
this.saveToDatabase();
|
||||||
pool: this.pool.toJsonable(),
|
|
||||||
head: this.head,
|
|
||||||
chatHead: this.chatHead,
|
|
||||||
publicStatus: this.publicStatus,
|
|
||||||
passwordHash: this.passwordHash});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//save all attributes to the database
|
||||||
|
Pad.prototype.saveToDatabase = function saveToDatabase(){
|
||||||
|
var dbObject = {};
|
||||||
|
|
||||||
|
for(var attr in this){
|
||||||
|
if(typeof this[attr] === "function") continue;
|
||||||
|
if(attributeBlackList.indexOf(attr) !== -1) continue;
|
||||||
|
|
||||||
|
dbObject[attr] = this[attr];
|
||||||
|
|
||||||
|
if(jsonableList.indexOf(attr) !== -1){
|
||||||
|
dbObject[attr] = dbObject[attr].toJsonable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
db.set("pad:"+this.id, dbObject);
|
||||||
|
}
|
||||||
|
|
||||||
Pad.prototype.getRevisionChangeset = function getRevisionChangeset(revNum, callback) {
|
Pad.prototype.getRevisionChangeset = function getRevisionChangeset(revNum, callback) {
|
||||||
db.getSub("pad:"+this.id+":revs:"+revNum, ["changeset"], callback);
|
db.getSub("pad:"+this.id+":revs:"+revNum, ["changeset"], callback);
|
||||||
};
|
};
|
||||||
|
@ -200,11 +218,10 @@ Pad.prototype.setText = function setText(newText) {
|
||||||
};
|
};
|
||||||
|
|
||||||
Pad.prototype.appendChatMessage = function appendChatMessage(text, userId, time) {
|
Pad.prototype.appendChatMessage = function appendChatMessage(text, userId, time) {
|
||||||
this.chatHead++;
|
this.chatHead++;
|
||||||
//save the chat entry in the database
|
//save the chat entry in the database
|
||||||
db.set("pad:"+this.id+":chat:"+this.chatHead, {"text": text, "userId": userId, "time": time});
|
db.set("pad:"+this.id+":chat:"+this.chatHead, {"text": text, "userId": userId, "time": time});
|
||||||
//save the new chat head
|
this.saveToDatabase();
|
||||||
db.setSub("pad:"+this.id, ["chatHead"], this.chatHead);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Pad.prototype.getChatMessage = function getChatMessage(entryNum, callback) {
|
Pad.prototype.getChatMessage = function getChatMessage(entryNum, callback) {
|
||||||
|
@ -324,27 +341,14 @@ Pad.prototype.init = function init(text, callback) {
|
||||||
//if this pad exists, load it
|
//if this pad exists, load it
|
||||||
if(value != null)
|
if(value != null)
|
||||||
{
|
{
|
||||||
_this.head = value.head;
|
//copy all attr. To a transfrom via fromJsonable if necassary
|
||||||
_this.atext = value.atext;
|
for(var attr in value){
|
||||||
_this.pool = _this.pool.fromJsonable(value.pool);
|
if(jsonableList.indexOf(attr) !== -1){
|
||||||
|
_this[attr] = _this[attr].fromJsonable(value[attr]);
|
||||||
//ensure we have a local chatHead variable
|
} else {
|
||||||
if(value.chatHead != null)
|
_this[attr] = value[attr];
|
||||||
_this.chatHead = value.chatHead;
|
}
|
||||||
else
|
}
|
||||||
_this.chatHead = -1;
|
|
||||||
|
|
||||||
//ensure we have a local publicStatus variable
|
|
||||||
if(value.publicStatus != null)
|
|
||||||
_this.publicStatus = value.publicStatus;
|
|
||||||
else
|
|
||||||
_this.publicStatus = false;
|
|
||||||
|
|
||||||
//ensure we have a local passwordHash variable
|
|
||||||
if(value.passwordHash != null)
|
|
||||||
_this.passwordHash = value.passwordHash;
|
|
||||||
else
|
|
||||||
_this.passwordHash = null;
|
|
||||||
}
|
}
|
||||||
//this pad doesn't exist, so create it
|
//this pad doesn't exist, so create it
|
||||||
else
|
else
|
||||||
|
@ -452,12 +456,12 @@ Pad.prototype.remove = function remove(callback) {
|
||||||
//set in db
|
//set in db
|
||||||
Pad.prototype.setPublicStatus = function setPublicStatus(publicStatus) {
|
Pad.prototype.setPublicStatus = function setPublicStatus(publicStatus) {
|
||||||
this.publicStatus = publicStatus;
|
this.publicStatus = publicStatus;
|
||||||
db.setSub("pad:"+this.id, ["publicStatus"], this.publicStatus);
|
this.saveToDatabase();
|
||||||
};
|
};
|
||||||
|
|
||||||
Pad.prototype.setPassword = function setPassword(password) {
|
Pad.prototype.setPassword = function setPassword(password) {
|
||||||
this.passwordHash = password == null ? null : hash(password, generateSalt());
|
this.passwordHash = password == null ? null : hash(password, generateSalt());
|
||||||
db.setSub("pad:"+this.id, ["passwordHash"], this.passwordHash);
|
this.saveToDatabase();
|
||||||
};
|
};
|
||||||
|
|
||||||
Pad.prototype.isCorrectPassword = function isCorrectPassword(password) {
|
Pad.prototype.isCorrectPassword = function isCorrectPassword(password) {
|
||||||
|
@ -468,6 +472,31 @@ Pad.prototype.isPasswordProtected = function isPasswordProtected() {
|
||||||
return this.passwordHash != null;
|
return this.passwordHash != null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Pad.prototype.addSavedRevision = function addSavedRevision(revNum, savedById, label) {
|
||||||
|
//if this revision is already saved, return silently
|
||||||
|
for(var i in this.savedRevisions){
|
||||||
|
if(this.savedRevisions.revNum === revNum){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//build the saved revision object
|
||||||
|
var savedRevision = {};
|
||||||
|
savedRevision.revNum = revNum;
|
||||||
|
savedRevision.savedById = savedById;
|
||||||
|
savedRevision.label = label || "Revision " + revNum;
|
||||||
|
savedRevision.timestamp = new Date().getTime();
|
||||||
|
savedRevision.id = randomString(10);
|
||||||
|
|
||||||
|
//save this new saved revision
|
||||||
|
this.savedRevisions.push(savedRevision);
|
||||||
|
this.saveToDatabase();
|
||||||
|
};
|
||||||
|
|
||||||
|
Pad.prototype.getSavedRevisions = function getSavedRevisions() {
|
||||||
|
return this.savedRevisions;
|
||||||
|
};
|
||||||
|
|
||||||
/* Crypto helper methods */
|
/* Crypto helper methods */
|
||||||
|
|
||||||
function hash(password, salt)
|
function hash(password, salt)
|
|
@ -18,11 +18,11 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var CommonCode = require('../utils/common_code');
|
|
||||||
var ERR = require("async-stacktrace");
|
var ERR = require("async-stacktrace");
|
||||||
var db = require("./DB").db;
|
var db = require("./DB").db;
|
||||||
var async = require("async");
|
var async = require("async");
|
||||||
var randomString = CommonCode.require('/pad_utils').randomString;
|
var randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns a read only id for a pad
|
* returns a read only id for a pad
|
|
@ -18,7 +18,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var CommonCode = require('../utils/common_code');
|
|
||||||
var ERR = require("async-stacktrace");
|
var ERR = require("async-stacktrace");
|
||||||
var db = require("./DB").db;
|
var db = require("./DB").db;
|
||||||
var async = require("async");
|
var async = require("async");
|
||||||
|
@ -26,7 +26,7 @@ var authorManager = require("./AuthorManager");
|
||||||
var padManager = require("./PadManager");
|
var padManager = require("./PadManager");
|
||||||
var sessionManager = require("./SessionManager");
|
var sessionManager = require("./SessionManager");
|
||||||
var settings = require("../utils/Settings")
|
var settings = require("../utils/Settings")
|
||||||
var randomString = CommonCode.require('/pad_utils').randomString;
|
var randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function controlls the access to a pad, it checks if the user can access a pad.
|
* This function controlls the access to a pad, it checks if the user can access a pad.
|
|
@ -18,10 +18,10 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var CommonCode = require('../utils/common_code');
|
|
||||||
var ERR = require("async-stacktrace");
|
var ERR = require("async-stacktrace");
|
||||||
var customError = require("../utils/customError");
|
var customError = require("../utils/customError");
|
||||||
var randomString = CommonCode.require('/pad_utils').randomString;
|
var randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString;
|
||||||
var db = require("./DB").db;
|
var db = require("./DB").db;
|
||||||
var async = require("async");
|
var async = require("async");
|
||||||
var groupMangager = require("./GroupManager");
|
var groupMangager = require("./GroupManager");
|
|
@ -20,9 +20,9 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var CommonCode = require('./utils/common_code');
|
|
||||||
var Changeset = CommonCode.require("/Changeset");
|
var Changeset = require("ep_etherpad-lite/static/js/Changeset");
|
||||||
var AttributePoolFactory = CommonCode.require("/AttributePoolFactory");
|
var AttributePool = require("ep_etherpad-lite/static/js/AttributePool");
|
||||||
|
|
||||||
function random() {
|
function random() {
|
||||||
this.nextInt = function (maxValue) {
|
this.nextInt = function (maxValue) {
|
||||||
|
@ -227,7 +227,7 @@ function runTests() {
|
||||||
return attribs; // it's already an attrib pool
|
return attribs; // it's already an attrib pool
|
||||||
} else {
|
} else {
|
||||||
// assume it's an array of attrib strings to be split and added
|
// assume it's an array of attrib strings to be split and added
|
||||||
var p = AttributePoolFactory.createAttributePool();
|
var p = new AttributePool();
|
||||||
attribs.forEach(function (kv) {
|
attribs.forEach(function (kv) {
|
||||||
p.putAttrib(kv.split(','));
|
p.putAttrib(kv.split(','));
|
||||||
});
|
});
|
||||||
|
@ -325,7 +325,7 @@ function runTests() {
|
||||||
runMutateAttributionTest(4, ['foo,bar', 'line,1', 'line,2', 'line,3', 'line,4', 'line,5'], "Z:5>1|2=2+1$x", ["?*1|1+1", "?*2|1+1", "*3|1+1", "?*4|1+1", "?*5|1+1"], ["?*1|1+1", "?*2|1+1", "+1*3|1+1", "?*4|1+1", "?*5|1+1"]);
|
runMutateAttributionTest(4, ['foo,bar', 'line,1', 'line,2', 'line,3', 'line,4', 'line,5'], "Z:5>1|2=2+1$x", ["?*1|1+1", "?*2|1+1", "*3|1+1", "?*4|1+1", "?*5|1+1"], ["?*1|1+1", "?*2|1+1", "+1*3|1+1", "?*4|1+1", "?*5|1+1"]);
|
||||||
|
|
||||||
var testPoolWithChars = (function () {
|
var testPoolWithChars = (function () {
|
||||||
var p = AttributePoolFactory.createAttributePool();
|
var p = new AttributePool();
|
||||||
p.putAttrib(['char', 'newline']);
|
p.putAttrib(['char', 'newline']);
|
||||||
for (var i = 1; i < 36; i++) {
|
for (var i = 1; i < 36; i++) {
|
||||||
p.putAttrib(['char', Changeset.numToString(i)]);
|
p.putAttrib(['char', Changeset.numToString(i)]);
|
||||||
|
@ -560,7 +560,7 @@ function runTests() {
|
||||||
var rand = new random();
|
var rand = new random();
|
||||||
print("> testCompose#" + randomSeed);
|
print("> testCompose#" + randomSeed);
|
||||||
|
|
||||||
var p = AttributePoolFactory.createAttributePool();
|
var p = new AttributePool();
|
||||||
|
|
||||||
var startText = randomMultiline(10, 20, rand) + '\n';
|
var startText = randomMultiline(10, 20, rand) + '\n';
|
||||||
|
|
||||||
|
@ -594,7 +594,7 @@ function runTests() {
|
||||||
|
|
||||||
(function simpleComposeAttributesTest() {
|
(function simpleComposeAttributesTest() {
|
||||||
print("> simpleComposeAttributesTest");
|
print("> simpleComposeAttributesTest");
|
||||||
var p = AttributePoolFactory.createAttributePool();
|
var p = new AttributePool();
|
||||||
p.putAttrib(['bold', '']);
|
p.putAttrib(['bold', '']);
|
||||||
p.putAttrib(['bold', 'true']);
|
p.putAttrib(['bold', 'true']);
|
||||||
var cs1 = Changeset.checkRep("Z:2>1*1+1*1=1$x");
|
var cs1 = Changeset.checkRep("Z:2>1*1+1*1=1$x");
|
||||||
|
@ -604,7 +604,7 @@ function runTests() {
|
||||||
})();
|
})();
|
||||||
|
|
||||||
(function followAttributesTest() {
|
(function followAttributesTest() {
|
||||||
var p = AttributePoolFactory.createAttributePool();
|
var p = new AttributePool();
|
||||||
p.putAttrib(['x', '']);
|
p.putAttrib(['x', '']);
|
||||||
p.putAttrib(['x', 'abc']);
|
p.putAttrib(['x', 'abc']);
|
||||||
p.putAttrib(['x', 'def']);
|
p.putAttrib(['x', 'def']);
|
||||||
|
@ -633,7 +633,7 @@ function runTests() {
|
||||||
var rand = new random();
|
var rand = new random();
|
||||||
print("> testFollow#" + randomSeed);
|
print("> testFollow#" + randomSeed);
|
||||||
|
|
||||||
var p = AttributePoolFactory.createAttributePool();
|
var p = new AttributePool();
|
||||||
|
|
||||||
var startText = randomMultiline(10, 20, rand) + '\n';
|
var startText = randomMultiline(10, 20, rand) + '\n';
|
||||||
|
|
||||||
|
@ -682,8 +682,8 @@ function runTests() {
|
||||||
(function testMoveOpsToNewPool() {
|
(function testMoveOpsToNewPool() {
|
||||||
print("> testMoveOpsToNewPool");
|
print("> testMoveOpsToNewPool");
|
||||||
|
|
||||||
var pool1 = AttributePoolFactory.createAttributePool();
|
var pool1 = new AttributePool();
|
||||||
var pool2 = AttributePoolFactory.createAttributePool();
|
var pool2 = new AttributePool();
|
||||||
|
|
||||||
pool1.putAttrib(['baz', 'qux']);
|
pool1.putAttrib(['baz', 'qux']);
|
||||||
pool1.putAttrib(['foo', 'bar']);
|
pool1.putAttrib(['foo', 'bar']);
|
||||||
|
@ -738,7 +738,7 @@ function runTests() {
|
||||||
(function testOpAttributeValue() {
|
(function testOpAttributeValue() {
|
||||||
print("> testOpAttributeValue");
|
print("> testOpAttributeValue");
|
||||||
|
|
||||||
var p = AttributePoolFactory.createAttributePool();
|
var p = new AttributePool();
|
||||||
p.putAttrib(['name', 'david']);
|
p.putAttrib(['name', 'david']);
|
||||||
p.putAttrib(['color', 'green']);
|
p.putAttrib(['color', 'green']);
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
a
|
||||||
|
<% e.begin_block("bar"); %>
|
||||||
|
A
|
||||||
|
<% e.begin_block("foo"); %>
|
||||||
|
XX
|
||||||
|
<% e.end_block(); %>
|
||||||
|
B
|
||||||
|
<% e.end_block(); %>
|
||||||
|
b
|
|
@ -0,0 +1,7 @@
|
||||||
|
<% e.inherit("./bar.ejs"); %>
|
||||||
|
|
||||||
|
<% e.begin_define_block("foo"); %>
|
||||||
|
YY
|
||||||
|
<% e.super(); %>
|
||||||
|
ZZ
|
||||||
|
<% e.end_define_block(); %>
|
|
@ -0,0 +1,115 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2011 RedHog (Egil Möller) <egil.moller@freecode.no>
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Basic usage:
|
||||||
|
*
|
||||||
|
* require("./index").require("./examples/foo.ejs")
|
||||||
|
*/
|
||||||
|
|
||||||
|
var ejs = require("ejs");
|
||||||
|
var fs = require("fs");
|
||||||
|
var path = require("path");
|
||||||
|
var hooks = require("ep_etherpad-lite/static/js/pluginfw/hooks.js");
|
||||||
|
|
||||||
|
exports.info = {
|
||||||
|
buf_stack: [],
|
||||||
|
block_stack: [],
|
||||||
|
blocks: {},
|
||||||
|
file_stack: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
exports._init = function (b, recursive) {
|
||||||
|
exports.info.buf_stack.push(exports.info.buf);
|
||||||
|
exports.info.buf = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
exports._exit = function (b, recursive) {
|
||||||
|
exports.info.file_stack[exports.info.file_stack.length-1].inherit.forEach(function (item) {
|
||||||
|
exports._require(item.name, item.args);
|
||||||
|
});
|
||||||
|
exports.info.buf = exports.info.buf_stack.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.begin_capture = function() {
|
||||||
|
exports.info.buf_stack.push(exports.info.buf.concat());
|
||||||
|
exports.info.buf.splice(0, exports.info.buf.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.end_capture = function () {
|
||||||
|
var res = exports.info.buf.join("");
|
||||||
|
exports.info.buf.splice.apply(
|
||||||
|
exports.info.buf,
|
||||||
|
[0, exports.info.buf.length].concat(exports.info.buf_stack.pop()));
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.begin_define_block = function (name) {
|
||||||
|
if (typeof exports.info.blocks[name] == "undefined")
|
||||||
|
exports.info.blocks[name] = {};
|
||||||
|
exports.info.block_stack.push(name);
|
||||||
|
exports.begin_capture();
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.super = function () {
|
||||||
|
exports.info.buf.push('<!eejs!super!>');
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.end_define_block = function () {
|
||||||
|
content = exports.end_capture();
|
||||||
|
var name = exports.info.block_stack.pop();
|
||||||
|
if (typeof exports.info.blocks[name].content == "undefined")
|
||||||
|
exports.info.blocks[name].content = content;
|
||||||
|
else if (typeof exports.info.blocks[name].content.indexOf('<!eejs!super!>'))
|
||||||
|
exports.info.blocks[name].content = exports.info.blocks[name].content.replace('<!eejs!super!>', content);
|
||||||
|
|
||||||
|
return exports.info.blocks[name].content;
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.end_block = function () {
|
||||||
|
var name = exports.info.block_stack[exports.info.block_stack.length-1];
|
||||||
|
var args = {content: exports.end_define_block()};
|
||||||
|
hooks.callAll("eejsBlock_" + name, args);
|
||||||
|
exports.info.buf.push(args.content);
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.begin_block = exports.begin_define_block;
|
||||||
|
|
||||||
|
exports.inherit = function (name, args) {
|
||||||
|
exports.info.file_stack[exports.info.file_stack.length-1].inherit.push({name:name, args:args});
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.require = function (name, args) {
|
||||||
|
if (args == undefined) args = {};
|
||||||
|
|
||||||
|
if ((name.indexOf("./") == 0 || name.indexOf("../") == 0) && exports.info.file_stack.length) {
|
||||||
|
name = path.join(path.dirname(exports.info.file_stack[exports.info.file_stack.length-1].path), name);
|
||||||
|
}
|
||||||
|
var ejspath = require.resolve(name)
|
||||||
|
|
||||||
|
args.e = exports;
|
||||||
|
args.require = require;
|
||||||
|
var template = '<% e._init(buf); %>' + fs.readFileSync(ejspath).toString() + '<% e._exit(); %>';
|
||||||
|
|
||||||
|
exports.info.file_stack.push({path: ejspath, inherit: []});
|
||||||
|
var res = ejs.render(template, args);
|
||||||
|
exports.info.file_stack.pop();
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
exports._require = function (name, args) {
|
||||||
|
exports.info.buf.push(exports.require(name, args));
|
||||||
|
}
|
|
@ -18,12 +18,12 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var CommonCode = require('../utils/common_code');
|
|
||||||
var ERR = require("async-stacktrace");
|
var ERR = require("async-stacktrace");
|
||||||
var fs = require("fs");
|
var fs = require("fs");
|
||||||
var api = require("../db/API");
|
var api = require("../db/API");
|
||||||
var padManager = require("../db/PadManager");
|
var padManager = require("../db/PadManager");
|
||||||
var randomString = CommonCode.require('/pad_utils').randomString;
|
var randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString;
|
||||||
|
|
||||||
//ensure we have an apikey
|
//ensure we have an apikey
|
||||||
var apikey = null;
|
var apikey = null;
|
|
@ -18,18 +18,21 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var CommonCode = require('../utils/common_code');
|
|
||||||
var ERR = require("async-stacktrace");
|
var ERR = require("async-stacktrace");
|
||||||
var async = require("async");
|
var async = require("async");
|
||||||
var padManager = require("../db/PadManager");
|
var padManager = require("../db/PadManager");
|
||||||
var Changeset = CommonCode.require("/Changeset");
|
var Changeset = require("ep_etherpad-lite/static/js/Changeset");
|
||||||
var AttributePoolFactory = CommonCode.require("/AttributePoolFactory");
|
var AttributePool = require("ep_etherpad-lite/static/js/AttributePool");
|
||||||
|
var AttributeManager = require("ep_etherpad-lite/static/js/AttributeManager");
|
||||||
var authorManager = require("../db/AuthorManager");
|
var authorManager = require("../db/AuthorManager");
|
||||||
var readOnlyManager = require("../db/ReadOnlyManager");
|
var readOnlyManager = require("../db/ReadOnlyManager");
|
||||||
var settings = require('../utils/Settings');
|
var settings = require('../utils/Settings');
|
||||||
var securityManager = require("../db/SecurityManager");
|
var securityManager = require("../db/SecurityManager");
|
||||||
|
var plugins = require("ep_etherpad-lite/static/js/pluginfw/plugins.js");
|
||||||
var log4js = require('log4js');
|
var log4js = require('log4js');
|
||||||
var messageLogger = log4js.getLogger("message");
|
var messageLogger = log4js.getLogger("message");
|
||||||
|
var _ = require('underscore');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A associative array that translates a session to a pad
|
* A associative array that translates a session to a pad
|
||||||
|
@ -127,7 +130,11 @@ exports.handleDisconnect = function(client)
|
||||||
//Go trough all user that are still on the pad, and send them the USER_LEAVE message
|
//Go trough all user that are still on the pad, and send them the USER_LEAVE message
|
||||||
for(i in pad2sessions[sessionPad])
|
for(i in pad2sessions[sessionPad])
|
||||||
{
|
{
|
||||||
socketio.sockets.sockets[pad2sessions[sessionPad][i]].json.send(messageToTheOtherUsers);
|
var socket = socketio.sockets.sockets[pad2sessions[sessionPad][i]];
|
||||||
|
if(socket !== undefined){
|
||||||
|
socket.json.send(messageToTheOtherUsers);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -185,6 +192,11 @@ exports.handleMessage = function(client, message)
|
||||||
{
|
{
|
||||||
handleChatMessage(client, message);
|
handleChatMessage(client, message);
|
||||||
}
|
}
|
||||||
|
else if(message.type == "COLLABROOM" &&
|
||||||
|
message.data.type == "SAVE_REVISION")
|
||||||
|
{
|
||||||
|
handleSaveRevisionMessage(client, message);
|
||||||
|
}
|
||||||
else if(message.type == "COLLABROOM" &&
|
else if(message.type == "COLLABROOM" &&
|
||||||
message.data.type == "CLIENT_MESSAGE" &&
|
message.data.type == "CLIENT_MESSAGE" &&
|
||||||
message.data.payload.type == "suggestUserName")
|
message.data.payload.type == "suggestUserName")
|
||||||
|
@ -198,6 +210,23 @@ exports.handleMessage = function(client, message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles a save revision message
|
||||||
|
* @param client the client that send this message
|
||||||
|
* @param message the message from the client
|
||||||
|
*/
|
||||||
|
function handleSaveRevisionMessage(client, message){
|
||||||
|
var padId = session2pad[client.id];
|
||||||
|
var userId = sessioninfos[client.id].author;
|
||||||
|
|
||||||
|
padManager.getPad(padId, function(err, pad)
|
||||||
|
{
|
||||||
|
if(ERR(err)) return;
|
||||||
|
|
||||||
|
pad.addSavedRevision(pad.head, userId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a Chat Message
|
* Handles a Chat Message
|
||||||
* @param client the client that send this message
|
* @param client the client that send this message
|
||||||
|
@ -367,7 +396,7 @@ function handleUserChanges(client, message)
|
||||||
|
|
||||||
//get all Vars we need
|
//get all Vars we need
|
||||||
var baseRev = message.data.baseRev;
|
var baseRev = message.data.baseRev;
|
||||||
var wireApool = (AttributePoolFactory.createAttributePool()).fromJsonable(message.data.apool);
|
var wireApool = (new AttributePool()).fromJsonable(message.data.apool);
|
||||||
var changeset = message.data.changeset;
|
var changeset = message.data.changeset;
|
||||||
|
|
||||||
var r, apool, pad;
|
var r, apool, pad;
|
||||||
|
@ -564,8 +593,12 @@ function _correctMarkersInPad(atext, apool) {
|
||||||
var offset = 0;
|
var offset = 0;
|
||||||
while (iter.hasNext()) {
|
while (iter.hasNext()) {
|
||||||
var op = iter.next();
|
var op = iter.next();
|
||||||
var listValue = Changeset.opAttributeValue(op, 'list', apool);
|
|
||||||
if (listValue) {
|
var hasMarker = _.find(AttributeManager.lineAttributes, function(attribute){
|
||||||
|
return Changeset.opAttributeValue(op, attribute, apool);
|
||||||
|
}) !== undefined;
|
||||||
|
|
||||||
|
if (hasMarker) {
|
||||||
for(var i=0;i<op.chars;i++) {
|
for(var i=0;i<op.chars;i++) {
|
||||||
if (offset > 0 && text.charAt(offset-1) != '\n') {
|
if (offset > 0 && text.charAt(offset-1) != '\n') {
|
||||||
badMarkers.push(offset);
|
badMarkers.push(offset);
|
||||||
|
@ -737,9 +770,10 @@ function handleClientReady(client, message)
|
||||||
{
|
{
|
||||||
for(var i in pad2sessions[message.padId])
|
for(var i in pad2sessions[message.padId])
|
||||||
{
|
{
|
||||||
if(sessioninfos[pad2sessions[message.padId][i]].author == author)
|
if(sessioninfos[pad2sessions[message.padId][i]] && sessioninfos[pad2sessions[message.padId][i]].author == author)
|
||||||
{
|
{
|
||||||
socketio.sockets.sockets[pad2sessions[message.padId][i]].json.send({disconnect:"userdup"});
|
var socket = socketio.sockets.sockets[pad2sessions[message.padId][i]];
|
||||||
|
if(socket) socket.json.send({disconnect:"userdup"});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -800,9 +834,12 @@ function handleClientReady(client, message)
|
||||||
"hideSidebar": false
|
"hideSidebar": false
|
||||||
},
|
},
|
||||||
"abiwordAvailable": settings.abiwordAvailable(),
|
"abiwordAvailable": settings.abiwordAvailable(),
|
||||||
"hooks": {}
|
"plugins": {
|
||||||
|
"plugins": plugins.plugins,
|
||||||
|
"parts": plugins.parts,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Add a username to the clientVars if one avaiable
|
//Add a username to the clientVars if one avaiable
|
||||||
if(authorName != null)
|
if(authorName != null)
|
||||||
{
|
{
|
|
@ -18,12 +18,12 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var CommonCode = require('../utils/common_code');
|
|
||||||
var ERR = require("async-stacktrace");
|
var ERR = require("async-stacktrace");
|
||||||
var async = require("async");
|
var async = require("async");
|
||||||
var padManager = require("../db/PadManager");
|
var padManager = require("../db/PadManager");
|
||||||
var Changeset = CommonCode.require("/Changeset");
|
var Changeset = require("ep_etherpad-lite/static/js/Changeset");
|
||||||
var AttributePoolFactory = CommonCode.require("/AttributePoolFactory");
|
var AttributePool = require("ep_etherpad-lite/static/js/AttributePool");
|
||||||
var settings = require('../utils/Settings');
|
var settings = require('../utils/Settings');
|
||||||
var authorManager = require("../db/AuthorManager");
|
var authorManager = require("../db/AuthorManager");
|
||||||
var log4js = require('log4js');
|
var log4js = require('log4js');
|
||||||
|
@ -166,6 +166,7 @@ function createTimesliderClientVars (padId, callback)
|
||||||
hooks: [],
|
hooks: [],
|
||||||
initialStyledContents: {}
|
initialStyledContents: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
var pad;
|
var pad;
|
||||||
var initialChangesets = [];
|
var initialChangesets = [];
|
||||||
|
|
||||||
|
@ -180,6 +181,12 @@ function createTimesliderClientVars (padId, callback)
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
//get all saved revisions and add them
|
||||||
|
function(callback)
|
||||||
|
{
|
||||||
|
clientVars.savedRevisions = pad.getSavedRevisions();
|
||||||
|
callback();
|
||||||
|
},
|
||||||
//get all authors and add them to
|
//get all authors and add them to
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
|
@ -265,7 +272,7 @@ function getChangesetInfo(padId, startNum, endNum, granularity, callback)
|
||||||
var forwardsChangesets = [];
|
var forwardsChangesets = [];
|
||||||
var backwardsChangesets = [];
|
var backwardsChangesets = [];
|
||||||
var timeDeltas = [];
|
var timeDeltas = [];
|
||||||
var apool = AttributePoolFactory.createAttributePool();
|
var apool = new AttributePool();
|
||||||
var pad;
|
var pad;
|
||||||
var composedChangesets = {};
|
var composedChangesets = {};
|
||||||
var revisionDate = [];
|
var revisionDate = [];
|
|
@ -0,0 +1,51 @@
|
||||||
|
var path = require('path');
|
||||||
|
var eejs = require('ep_etherpad-lite/node/eejs');
|
||||||
|
var installer = require('ep_etherpad-lite/static/js/pluginfw/installer');
|
||||||
|
var plugins = require('ep_etherpad-lite/static/js/pluginfw/plugins');
|
||||||
|
|
||||||
|
exports.expressCreateServer = function (hook_name, args, cb) {
|
||||||
|
args.app.get('/admin/plugins', function(req, res) {
|
||||||
|
var plugins = require("ep_etherpad-lite/static/js/pluginfw/plugins");
|
||||||
|
var render_args = {
|
||||||
|
plugins: plugins.plugins,
|
||||||
|
search_results: {},
|
||||||
|
errors: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
res.send(eejs.require(
|
||||||
|
"ep_etherpad-lite/templates/admin/plugins.html",
|
||||||
|
render_args), {});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.socketio = function (hook_name, args, cb) {
|
||||||
|
var io = args.io.of("/pluginfw/installer");
|
||||||
|
io.on('connection', function (socket) {
|
||||||
|
socket.on("load", function (query) {
|
||||||
|
socket.emit("installed-results", {results: plugins.plugins});
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("search", function (query) {
|
||||||
|
socket.emit("progress", {progress:0, message:'Fetching results...'});
|
||||||
|
installer.search(query, function (progress) {
|
||||||
|
if (progress.results)
|
||||||
|
socket.emit("search-result", progress);
|
||||||
|
socket.emit("progress", progress);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("install", function (plugin_name) {
|
||||||
|
socket.emit("progress", {progress:0, message:'Downloading and installing ' + plugin_name + "..."});
|
||||||
|
installer.install(plugin_name, function (progress) {
|
||||||
|
socket.emit("progress", progress);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("uninstall", function (plugin_name) {
|
||||||
|
socket.emit("progress", {progress:0, message:'Uninstalling ' + plugin_name + "..."});
|
||||||
|
installer.uninstall(plugin_name, function (progress) {
|
||||||
|
socket.emit("progress", progress);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
var log4js = require('log4js');
|
||||||
|
var apiLogger = log4js.getLogger("API");
|
||||||
|
var formidable = require('formidable');
|
||||||
|
var apiHandler = require('../../handler/APIHandler');
|
||||||
|
|
||||||
|
//This is for making an api call, collecting all post information and passing it to the apiHandler
|
||||||
|
var apiCaller = function(req, res, fields) {
|
||||||
|
res.header("Content-Type", "application/json; charset=utf-8");
|
||||||
|
|
||||||
|
apiLogger.info("REQUEST, " + req.params.func + ", " + JSON.stringify(fields));
|
||||||
|
|
||||||
|
//wrap the send function so we can log the response
|
||||||
|
//note: res._send seems to be already in use, so better use a "unique" name
|
||||||
|
res._____send = res.send;
|
||||||
|
res.send = function (response) {
|
||||||
|
response = JSON.stringify(response);
|
||||||
|
apiLogger.info("RESPONSE, " + req.params.func + ", " + response);
|
||||||
|
|
||||||
|
//is this a jsonp call, if yes, add the function call
|
||||||
|
if(req.query.jsonp)
|
||||||
|
response = req.query.jsonp + "(" + response + ")";
|
||||||
|
|
||||||
|
res._____send(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
//call the api handler
|
||||||
|
apiHandler.handle(req.params.func, fields, req, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.apiCaller = apiCaller;
|
||||||
|
|
||||||
|
exports.expressCreateServer = function (hook_name, args, cb) {
|
||||||
|
//This is a api GET call, collect all post informations and pass it to the apiHandler
|
||||||
|
args.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
|
||||||
|
args.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
|
||||||
|
args.app.post('/ep/pad/connection-diagnostic-info', function(req, res) {
|
||||||
|
new formidable.IncomingForm().parse(req, function(err, fields, files) {
|
||||||
|
console.log("DIAGNOSTIC-INFO: " + fields.diagnosticInfo);
|
||||||
|
res.end("OK");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//The Etherpad client side sends information about client side javscript errors
|
||||||
|
args.app.post('/jserror', function(req, res) {
|
||||||
|
new formidable.IncomingForm().parse(req, function(err, fields, files) {
|
||||||
|
console.error("CLIENT SIDE JAVASCRIPT ERROR: " + fields.errorInfo);
|
||||||
|
res.end("OK");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
var os = require("os");
|
||||||
|
var db = require('../../db/DB');
|
||||||
|
|
||||||
|
|
||||||
|
exports.onShutdown = false;
|
||||||
|
exports.gracefulShutdown = function(err) {
|
||||||
|
if(err && err.stack) {
|
||||||
|
console.error(err.stack);
|
||||||
|
} else if(err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
//ensure there is only one graceful shutdown running
|
||||||
|
if(exports.onShutdown) return;
|
||||||
|
exports.onShutdown = true;
|
||||||
|
|
||||||
|
console.log("graceful shutdown...");
|
||||||
|
|
||||||
|
//stop the http server
|
||||||
|
exports.app.close();
|
||||||
|
|
||||||
|
//do the db shutdown
|
||||||
|
db.db.doShutdown(function() {
|
||||||
|
console.log("db sucessfully closed.");
|
||||||
|
|
||||||
|
process.exit(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(function(){
|
||||||
|
process.exit(1);
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
exports.expressCreateServer = function (hook_name, args, cb) {
|
||||||
|
exports.app = args.app;
|
||||||
|
|
||||||
|
args.app.error(function(err, req, res, next){
|
||||||
|
res.send(500);
|
||||||
|
console.error(err.stack ? err.stack : err.toString());
|
||||||
|
exports.gracefulShutdown();
|
||||||
|
});
|
||||||
|
|
||||||
|
//connect graceful shutdown with sigint and uncaughtexception
|
||||||
|
if(os.type().indexOf("Windows") == -1) {
|
||||||
|
//sigint is so far not working on windows
|
||||||
|
//https://github.com/joyent/node/issues/1553
|
||||||
|
process.on('SIGINT', exports.gracefulShutdown);
|
||||||
|
}
|
||||||
|
|
||||||
|
process.on('uncaughtException', exports.gracefulShutdown);
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
var hasPadAccess = require("../../padaccess");
|
||||||
|
var settings = require('../../utils/Settings');
|
||||||
|
var exportHandler = require('../../handler/ExportHandler');
|
||||||
|
var importHandler = require('../../handler/ImportHandler');
|
||||||
|
|
||||||
|
exports.expressCreateServer = function (hook_name, args, cb) {
|
||||||
|
args.app.get('/p/:pad/:rev?/export/:type', function(req, res, next) {
|
||||||
|
var types = ["pdf", "doc", "txt", "html", "odt", "dokuwiki"];
|
||||||
|
//send a 404 if we don't support this filetype
|
||||||
|
if (types.indexOf(req.params.type) == -1) {
|
||||||
|
next();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if abiword is disabled, and this is a format we only support with abiword, output a message
|
||||||
|
if (settings.abiword == null &&
|
||||||
|
["odt", "pdf", "doc"].indexOf(req.params.type) !== -1) {
|
||||||
|
res.send("Abiword is not enabled at this Etherpad Lite instance. Set the path to Abiword in settings.json to enable this feature");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.header("Access-Control-Allow-Origin", "*");
|
||||||
|
|
||||||
|
hasPadAccess(req, res, function() {
|
||||||
|
exportHandler.doExport(req, res, req.params.pad, req.params.type);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//handle import requests
|
||||||
|
args.app.post('/p/:pad/import', function(req, res, next) {
|
||||||
|
//if abiword is disabled, skip handling this request
|
||||||
|
if(settings.abiword == null) {
|
||||||
|
next();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasPadAccess(req, res, function() {
|
||||||
|
importHandler.doImport(req, res, req.params.pad);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
var async = require('async');
|
||||||
|
var ERR = require("async-stacktrace");
|
||||||
|
var readOnlyManager = require("../../db/ReadOnlyManager");
|
||||||
|
var hasPadAccess = require("../../padaccess");
|
||||||
|
var exporthtml = require("../../utils/ExportHtml");
|
||||||
|
|
||||||
|
exports.expressCreateServer = function (hook_name, args, cb) {
|
||||||
|
//serve read only pad
|
||||||
|
args.app.get('/ro/:id', function(req, res)
|
||||||
|
{
|
||||||
|
var html;
|
||||||
|
var padId;
|
||||||
|
var pad;
|
||||||
|
|
||||||
|
async.series([
|
||||||
|
//translate the read only pad to a padId
|
||||||
|
function(callback)
|
||||||
|
{
|
||||||
|
readOnlyManager.getPadId(req.params.id, function(err, _padId)
|
||||||
|
{
|
||||||
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
|
padId = _padId;
|
||||||
|
|
||||||
|
//we need that to tell hasPadAcess about the pad
|
||||||
|
req.params.pad = padId;
|
||||||
|
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
//render the html document
|
||||||
|
function(callback)
|
||||||
|
{
|
||||||
|
//return if the there is no padId
|
||||||
|
if(padId == null)
|
||||||
|
{
|
||||||
|
callback("notfound");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasPadAccess(req, res, function()
|
||||||
|
{
|
||||||
|
//render the html document
|
||||||
|
exporthtml.getPadHTMLDocument(padId, null, false, function(err, _html)
|
||||||
|
{
|
||||||
|
if(ERR(err, callback)) return;
|
||||||
|
html = _html;
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
], function(err)
|
||||||
|
{
|
||||||
|
//throw any unexpected error
|
||||||
|
if(err && err != "notfound")
|
||||||
|
ERR(err);
|
||||||
|
|
||||||
|
if(err == "notfound")
|
||||||
|
res.send('404 - Not Found', 404);
|
||||||
|
else
|
||||||
|
res.send(html);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
var padManager = require('../../db/PadManager');
|
||||||
|
|
||||||
|
exports.expressCreateServer = function (hook_name, args, cb) {
|
||||||
|
//redirects browser to the pad's sanitized url if needed. otherwise, renders the html
|
||||||
|
args.app.param('pad', function (req, res, next, padId) {
|
||||||
|
//ensure the padname is valid and the url doesn't end with a /
|
||||||
|
if(!padManager.isValidPadId(padId) || /\/$/.test(req.url))
|
||||||
|
{
|
||||||
|
res.send('Such a padname is forbidden', 404);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
padManager.sanitizePadId(padId, function(sanitizedPadId) {
|
||||||
|
//the pad id was sanitized, so we redirect to the sanitized version
|
||||||
|
if(sanitizedPadId != padId)
|
||||||
|
{
|
||||||
|
var real_path = req.path.replace(/^\/p\/[^\/]+/, '/p/' + sanitizedPadId);
|
||||||
|
res.header('Location', real_path);
|
||||||
|
res.send('You should be redirected to <a href="' + real_path + '">' + real_path + '</a>', 302);
|
||||||
|
}
|
||||||
|
//the pad id was fine, so just render it
|
||||||
|
else
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
var log4js = require('log4js');
|
||||||
|
var socketio = require('socket.io');
|
||||||
|
var settings = require('../../utils/Settings');
|
||||||
|
var socketIORouter = require("../../handler/SocketIORouter");
|
||||||
|
var hooks = require("ep_etherpad-lite/static/js/pluginfw/hooks");
|
||||||
|
|
||||||
|
var padMessageHandler = require("../../handler/PadMessageHandler");
|
||||||
|
var timesliderMessageHandler = require("../../handler/TimesliderMessageHandler");
|
||||||
|
|
||||||
|
|
||||||
|
exports.expressCreateServer = function (hook_name, args, cb) {
|
||||||
|
//init socket.io and redirect all requests to the MessageHandler
|
||||||
|
var io = socketio.listen(args.app);
|
||||||
|
|
||||||
|
//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
|
||||||
|
io.set('transports', ['xhr-polling']);
|
||||||
|
|
||||||
|
var socketIOLogger = log4js.getLogger("socket.io");
|
||||||
|
io.set('logger', {
|
||||||
|
debug: function (str)
|
||||||
|
{
|
||||||
|
socketIOLogger.debug.apply(socketIOLogger, arguments);
|
||||||
|
},
|
||||||
|
info: function (str)
|
||||||
|
{
|
||||||
|
socketIOLogger.info.apply(socketIOLogger, arguments);
|
||||||
|
},
|
||||||
|
warn: function (str)
|
||||||
|
{
|
||||||
|
socketIOLogger.warn.apply(socketIOLogger, arguments);
|
||||||
|
},
|
||||||
|
error: function (str)
|
||||||
|
{
|
||||||
|
socketIOLogger.error.apply(socketIOLogger, arguments);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
//minify socket.io javascript
|
||||||
|
if(settings.minify)
|
||||||
|
io.enable('browser client minification');
|
||||||
|
|
||||||
|
//Initalize the Socket.IO Router
|
||||||
|
socketIORouter.setSocketIO(io);
|
||||||
|
socketIORouter.addComponent("pad", padMessageHandler);
|
||||||
|
socketIORouter.addComponent("timeslider", timesliderMessageHandler);
|
||||||
|
|
||||||
|
hooks.callAll("socketio", {"app": args.app, "io": io});
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
var path = require('path');
|
||||||
|
var eejs = require('ep_etherpad-lite/node/eejs');
|
||||||
|
|
||||||
|
exports.expressCreateServer = function (hook_name, args, cb) {
|
||||||
|
|
||||||
|
//serve index.html under /
|
||||||
|
args.app.get('/', function(req, res)
|
||||||
|
{
|
||||||
|
res.send(eejs.require("ep_etherpad-lite/templates/index.html"));
|
||||||
|
});
|
||||||
|
|
||||||
|
//serve robots.txt
|
||||||
|
args.app.get('/robots.txt', function(req, res)
|
||||||
|
{
|
||||||
|
var filePath = path.normalize(__dirname + "/../../../static/robots.txt");
|
||||||
|
res.sendfile(filePath);
|
||||||
|
});
|
||||||
|
|
||||||
|
//serve favicon.ico
|
||||||
|
args.app.get('/favicon.ico', function(req, res)
|
||||||
|
{
|
||||||
|
var filePath = path.normalize(__dirname + "/../../../static/custom/favicon.ico");
|
||||||
|
res.sendfile(filePath, function(err)
|
||||||
|
{
|
||||||
|
//there is no custom favicon, send the default favicon
|
||||||
|
if(err)
|
||||||
|
{
|
||||||
|
filePath = path.normalize(__dirname + "/../../../static/favicon.ico");
|
||||||
|
res.sendfile(filePath);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//serve pad.html under /p
|
||||||
|
args.app.get('/p/:pad', function(req, res, next)
|
||||||
|
{
|
||||||
|
res.send(eejs.require("ep_etherpad-lite/templates/pad.html"));
|
||||||
|
});
|
||||||
|
|
||||||
|
//serve timeslider.html under /p/$padname/timeslider
|
||||||
|
args.app.get('/p/:pad/timeslider', function(req, res, next)
|
||||||
|
{
|
||||||
|
res.send(eejs.require("ep_etherpad-lite/templates/timeslider.html"));
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
var path = require('path');
|
||||||
|
var minify = require('../../utils/Minify');
|
||||||
|
var plugins = require("ep_etherpad-lite/static/js/pluginfw/plugins");
|
||||||
|
var CachingMiddleware = require('../../utils/caching_middleware');
|
||||||
|
var settings = require("../../utils/Settings");
|
||||||
|
var Yajsml = require('yajsml');
|
||||||
|
var fs = require("fs");
|
||||||
|
var ERR = require("async-stacktrace");
|
||||||
|
var _ = require("underscore");
|
||||||
|
|
||||||
|
exports.expressCreateServer = function (hook_name, args, cb) {
|
||||||
|
// Cache both minified and static.
|
||||||
|
var assetCache = new CachingMiddleware;
|
||||||
|
args.app.all('/(javascripts|static)/*', assetCache.handle);
|
||||||
|
|
||||||
|
// Minify will serve static files compressed (minify enabled). It also has
|
||||||
|
// file-specific hacks for ace/require-kernel/etc.
|
||||||
|
args.app.all('/static/:filename(*)', minify.minify);
|
||||||
|
|
||||||
|
// Setup middleware that will package JavaScript files served by minify for
|
||||||
|
// CommonJS loader on the client-side.
|
||||||
|
var jsServer = new (Yajsml.Server)({
|
||||||
|
rootPath: 'javascripts/src/'
|
||||||
|
, rootURI: 'http://localhost:' + settings.port + '/static/js/'
|
||||||
|
, libraryPath: 'javascripts/lib/'
|
||||||
|
, libraryURI: 'http://localhost:' + settings.port + '/static/plugins/'
|
||||||
|
});
|
||||||
|
|
||||||
|
var StaticAssociator = Yajsml.associators.StaticAssociator;
|
||||||
|
var associations =
|
||||||
|
Yajsml.associators.associationsForSimpleMapping(minify.tar);
|
||||||
|
var associator = new StaticAssociator(associations);
|
||||||
|
jsServer.setAssociator(associator);
|
||||||
|
args.app.use(jsServer);
|
||||||
|
|
||||||
|
// serve plugin definitions
|
||||||
|
// not very static, but served here so that client can do require("pluginfw/static/js/plugin-definitions.js");
|
||||||
|
args.app.get('/pluginfw/plugin-definitions.json', function (req, res, next) {
|
||||||
|
|
||||||
|
var clientParts = _(plugins.parts)
|
||||||
|
.filter(function(part){ return _(part).has('client_hooks') });
|
||||||
|
|
||||||
|
var clientPlugins = {};
|
||||||
|
|
||||||
|
_(clientParts).chain()
|
||||||
|
.map(function(part){ return part.plugin })
|
||||||
|
.uniq()
|
||||||
|
.each(function(name){
|
||||||
|
clientPlugins[name] = _(plugins.plugins[name]).clone();
|
||||||
|
delete clientPlugins[name]['package'];
|
||||||
|
});
|
||||||
|
|
||||||
|
res.header("Content-Type","application/json; charset=utf-8");
|
||||||
|
res.write(JSON.stringify({"plugins": clientPlugins, "parts": clientParts}));
|
||||||
|
res.end();
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
var express = require('express');
|
||||||
|
var log4js = require('log4js');
|
||||||
|
var httpLogger = log4js.getLogger("http");
|
||||||
|
var settings = require('../../utils/Settings');
|
||||||
|
|
||||||
|
|
||||||
|
//checks for basic http auth
|
||||||
|
exports.basicAuth = function (req, res, next) {
|
||||||
|
|
||||||
|
// When handling HTTP-Auth, an undefined password will lead to no authorization at all
|
||||||
|
var pass = settings.httpAuth || '';
|
||||||
|
|
||||||
|
if (req.path.indexOf('/admin') == 0) {
|
||||||
|
var pass = settings.adminHttpAuth;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Just pass if password is an empty string
|
||||||
|
if (pass === '') {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// If a password has been set and auth headers are present...
|
||||||
|
if (pass && req.headers.authorization && req.headers.authorization.search('Basic ') === 0) {
|
||||||
|
// ...check login and password
|
||||||
|
if (new Buffer(req.headers.authorization.split(' ')[1], 'base64').toString() === pass) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise return Auth required Headers, delayed for 1 second, if auth failed.
|
||||||
|
res.header('WWW-Authenticate', 'Basic realm="Protected Area"');
|
||||||
|
if (req.headers.authorization) {
|
||||||
|
setTimeout(function () {
|
||||||
|
res.send('Authentication required', 401);
|
||||||
|
}, 1000);
|
||||||
|
} else {
|
||||||
|
res.send('Authentication required', 401);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.expressConfigure = function (hook_name, args, cb) {
|
||||||
|
args.app.use(exports.basicAuth);
|
||||||
|
|
||||||
|
// 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"))
|
||||||
|
args.app.use(log4js.connectLogger(httpLogger, { level: log4js.levels.INFO, format: ':status, :method :url'}));
|
||||||
|
args.app.use(express.cookieParser());
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
var ERR = require("async-stacktrace");
|
||||||
|
var securityManager = require('./db/SecurityManager');
|
||||||
|
|
||||||
|
//checks for padAccess
|
||||||
|
module.exports = function (req, res, callback) {
|
||||||
|
|
||||||
|
// FIXME: Why is this ever undefined??
|
||||||
|
if (req.cookies === undefined) req.cookies = {};
|
||||||
|
|
||||||
|
securityManager.checkAccess(req.params.pad, req.cookies.sessionid, req.cookies.token, req.cookies.password, function(err, accessObj) {
|
||||||
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
|
//there is access, continue
|
||||||
|
if(accessObj.accessStatus == "grant") {
|
||||||
|
callback();
|
||||||
|
//no access
|
||||||
|
} else {
|
||||||
|
res.send("403 - Can't touch this", 403);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,99 @@
|
||||||
|
/**
|
||||||
|
* This module is started with bin/run.sh. It sets up a Express HTTP and a Socket.IO Server.
|
||||||
|
* Static file Requests are answered directly from this module, Socket.IO messages are passed
|
||||||
|
* to MessageHandler and minfied requests are passed to minified.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 2011 Peter 'Pita' Martischka (Primary Technology Ltd)
|
||||||
|
*
|
||||||
|
* 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 log4js = require('log4js');
|
||||||
|
var fs = require('fs');
|
||||||
|
var settings = require('./utils/Settings');
|
||||||
|
var db = require('./db/DB');
|
||||||
|
var async = require('async');
|
||||||
|
var express = require('express');
|
||||||
|
var path = require('path');
|
||||||
|
var plugins = require("ep_etherpad-lite/static/js/pluginfw/plugins");
|
||||||
|
var hooks = require("ep_etherpad-lite/static/js/pluginfw/hooks");
|
||||||
|
var npm = require("npm/lib/npm.js");
|
||||||
|
|
||||||
|
//try to get the git version
|
||||||
|
var version = "";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var rootPath = path.resolve(npm.dir, '..');
|
||||||
|
var ref = fs.readFileSync(rootPath + "/.git/HEAD", "utf-8");
|
||||||
|
var refPath = rootPath + "/.git/" + ref.substring(5, ref.indexOf("\n"));
|
||||||
|
version = fs.readFileSync(refPath, "utf-8");
|
||||||
|
version = version.substring(0, 7);
|
||||||
|
console.log("Your Etherpad Lite git version is " + version);
|
||||||
|
}
|
||||||
|
catch(e)
|
||||||
|
{
|
||||||
|
console.warn("Can't get git version for server header\n" + e.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Report bugs at https://github.com/Pita/etherpad-lite/issues")
|
||||||
|
|
||||||
|
var serverName = "Etherpad-Lite " + version + " (http://j.mp/ep-lite)";
|
||||||
|
|
||||||
|
//set loglevel
|
||||||
|
log4js.setGlobalLogLevel(settings.loglevel);
|
||||||
|
|
||||||
|
async.waterfall([
|
||||||
|
//initalize the database
|
||||||
|
function (callback)
|
||||||
|
{
|
||||||
|
db.init(callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
plugins.update,
|
||||||
|
|
||||||
|
function (callback) {
|
||||||
|
console.log("Installed plugins: " + plugins.formatPlugins());
|
||||||
|
console.log("Installed parts:\n" + plugins.formatParts());
|
||||||
|
console.log("Installed hooks:\n" + plugins.formatHooks());
|
||||||
|
callback();
|
||||||
|
},
|
||||||
|
|
||||||
|
//initalize the http server
|
||||||
|
function (callback)
|
||||||
|
{
|
||||||
|
//create server
|
||||||
|
var app = express.createServer();
|
||||||
|
|
||||||
|
app.use(function (req, res, next) {
|
||||||
|
res.header("Server", serverName);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
|
app.configure(function() { hooks.callAll("expressConfigure", {"app": app}); });
|
||||||
|
|
||||||
|
hooks.callAll("expressCreateServer", {"app": app});
|
||||||
|
|
||||||
|
//let the server listen
|
||||||
|
app.listen(settings.port, settings.ip);
|
||||||
|
console.log("Server is listening at " + settings.ip + ":" + settings.port);
|
||||||
|
if(settings.adminHttpAuth){
|
||||||
|
console.log("Plugin admin page listening at " + settings.ip + ":" + settings.port + "/admin/plugins");
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
console.log("Admin username and password not set in settings.json. To access admin please uncomment and edit adminHttpAuth in settings.json");
|
||||||
|
}
|
||||||
|
callback(null);
|
||||||
|
}
|
||||||
|
]);
|
|
@ -15,8 +15,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var async = require("async");
|
var async = require("async");
|
||||||
var CommonCode = require('./common_code');
|
|
||||||
var Changeset = CommonCode.require("/Changeset");
|
var Changeset = require("ep_etherpad-lite/static/js/Changeset");
|
||||||
var padManager = require("../db/PadManager");
|
var padManager = require("../db/PadManager");
|
||||||
|
|
||||||
function getPadDokuWiki(pad, revNum, callback)
|
function getPadDokuWiki(pad, revNum, callback)
|
|
@ -14,12 +14,12 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var CommonCode = require('./common_code');
|
|
||||||
var async = require("async");
|
var async = require("async");
|
||||||
var Changeset = CommonCode.require("/Changeset");
|
var Changeset = require("ep_etherpad-lite/static/js/Changeset");
|
||||||
var padManager = require("../db/PadManager");
|
var padManager = require("../db/PadManager");
|
||||||
var ERR = require("async-stacktrace");
|
var ERR = require("async-stacktrace");
|
||||||
var Security = CommonCode.require('/security');
|
var Security = require('ep_etherpad-lite/static/js/security');
|
||||||
|
|
||||||
function getPadPlainText(pad, revNum)
|
function getPadPlainText(pad, revNum)
|
||||||
{
|
{
|
|
@ -17,10 +17,10 @@
|
||||||
var jsdom = require('jsdom-nocontextifiy').jsdom;
|
var jsdom = require('jsdom-nocontextifiy').jsdom;
|
||||||
var log4js = require('log4js');
|
var log4js = require('log4js');
|
||||||
|
|
||||||
var CommonCode = require('../utils/common_code');
|
|
||||||
var Changeset = CommonCode.require("/Changeset");
|
var Changeset = require("ep_etherpad-lite/static/js/Changeset");
|
||||||
var contentcollector = CommonCode.require("/contentcollector");
|
var contentcollector = require("ep_etherpad-lite/static/js/contentcollector");
|
||||||
var map = CommonCode.require("/ace2_common").map;
|
var map = require("ep_etherpad-lite/static/js/ace2_common").map;
|
||||||
|
|
||||||
function setPadHTML(pad, html, callback)
|
function setPadHTML(pad, html, callback)
|
||||||
{
|
{
|
|
@ -27,19 +27,22 @@ var cleanCSS = require('clean-css');
|
||||||
var jsp = require("uglify-js").parser;
|
var jsp = require("uglify-js").parser;
|
||||||
var pro = require("uglify-js").uglify;
|
var pro = require("uglify-js").uglify;
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
|
var plugins = require("ep_etherpad-lite/static/js/pluginfw/plugins");
|
||||||
var RequireKernel = require('require-kernel');
|
var RequireKernel = require('require-kernel');
|
||||||
var server = require('../server');
|
|
||||||
|
|
||||||
var ROOT_DIR = path.normalize(__dirname + "/../../static/");
|
var ROOT_DIR = path.normalize(__dirname + "/../../static/");
|
||||||
var TAR_PATH = path.join(__dirname, 'tar.json');
|
var TAR_PATH = path.join(__dirname, 'tar.json');
|
||||||
var tar = JSON.parse(fs.readFileSync(TAR_PATH, 'utf8'));
|
var tar = JSON.parse(fs.readFileSync(TAR_PATH, 'utf8'));
|
||||||
|
|
||||||
// Rewrite tar to include modules with no extensions and proper rooted paths.
|
// Rewrite tar to include modules with no extensions and proper rooted paths.
|
||||||
|
var LIBRARY_PREFIX = 'ep_etherpad-lite/static/js';
|
||||||
exports.tar = {};
|
exports.tar = {};
|
||||||
for (var key in tar) {
|
for (var key in tar) {
|
||||||
exports.tar['/' + key] =
|
exports.tar[LIBRARY_PREFIX + '/' + key] =
|
||||||
tar[key].map(function (p) {return '/' + p}).concat(
|
tar[key].map(function (p) {return LIBRARY_PREFIX + '/' + p}).concat(
|
||||||
tar[key].map(function (p) {return '/' + p.replace(/\.js$/, '')})
|
tar[key].map(function (p) {
|
||||||
|
return LIBRARY_PREFIX + '/' + p.replace(/\.js$/, '')
|
||||||
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,6 +66,22 @@ exports.minify = function(req, res, next)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Handle static files for plugins:
|
||||||
|
paths like "plugins/ep_myplugin/static/js/test.js"
|
||||||
|
are rewritten into ROOT_PATH_OF_MYPLUGIN/static/js/test.js,
|
||||||
|
commonly ETHERPAD_ROOT/node_modules/ep_myplugin/static/js/test.js
|
||||||
|
*/
|
||||||
|
var match = filename.match(/^plugins\/([^\/]+)\/static\/(.*)/);
|
||||||
|
if (match) {
|
||||||
|
var pluginName = match[1];
|
||||||
|
var resourcePath = match[2];
|
||||||
|
var plugin = plugins.plugins[pluginName];
|
||||||
|
if (plugin) {
|
||||||
|
var pluginPath = plugin.package.realPath;
|
||||||
|
filename = path.relative(ROOT_DIR, pluginPath + '/static/' + resourcePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// What content type should this be?
|
// What content type should this be?
|
||||||
// TODO: This should use a MIME module.
|
// TODO: This should use a MIME module.
|
||||||
var contentType;
|
var contentType;
|
||||||
|
@ -89,10 +108,10 @@ exports.minify = function(req, res, next)
|
||||||
date = new Date(date);
|
date = new Date(date);
|
||||||
res.setHeader('last-modified', date.toUTCString());
|
res.setHeader('last-modified', date.toUTCString());
|
||||||
res.setHeader('date', (new Date()).toUTCString());
|
res.setHeader('date', (new Date()).toUTCString());
|
||||||
if (server.maxAge) {
|
if (settings.maxAge !== undefined) {
|
||||||
var expiresDate = new Date((new Date()).getTime()+server.maxAge*1000);
|
var expiresDate = new Date((new Date()).getTime()+settings.maxAge*1000);
|
||||||
res.setHeader('expires', expiresDate.toUTCString());
|
res.setHeader('expires', expiresDate.toUTCString());
|
||||||
res.setHeader('cache-control', 'max-age=' + server.maxAge);
|
res.setHeader('cache-control', 'max-age=' + settings.maxAge);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,7 +131,10 @@ exports.minify = function(req, res, next)
|
||||||
res.end();
|
res.end();
|
||||||
} else if (req.method == 'GET') {
|
} else if (req.method == 'GET') {
|
||||||
getFileCompressed(filename, contentType, function (error, content) {
|
getFileCompressed(filename, contentType, function (error, content) {
|
||||||
if(ERR(error)) return;
|
if(ERR(error, function(){
|
||||||
|
res.writeHead(500, {});
|
||||||
|
res.end();
|
||||||
|
})) return;
|
||||||
res.header("Content-Type", contentType);
|
res.header("Content-Type", contentType);
|
||||||
res.writeHead(200, {});
|
res.writeHead(200, {});
|
||||||
res.write(content);
|
res.write(content);
|
|
@ -0,0 +1,513 @@
|
||||||
|
/**
|
||||||
|
* This Module manages all /minified/* requests. It controls the
|
||||||
|
* minification && compression of Javascript and CSS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 2011 Peter 'Pita' Martischka (Primary Technology Ltd)
|
||||||
|
*
|
||||||
|
* 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 ERR = require("async-stacktrace");
|
||||||
|
var settings = require('./Settings');
|
||||||
|
var async = require('async');
|
||||||
|
var fs = require('fs');
|
||||||
|
var cleanCSS = require('clean-css');
|
||||||
|
var jsp = require("uglify-js").parser;
|
||||||
|
var pro = require("uglify-js").uglify;
|
||||||
|
var path = require('path');
|
||||||
|
var RequireKernel = require('require-kernel');
|
||||||
|
var server = require('../server');
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
|
var ROOT_DIR = path.normalize(__dirname + "/../" );
|
||||||
|
var JS_DIR = ROOT_DIR + '../static/js/';
|
||||||
|
var CSS_DIR = ROOT_DIR + '../static/css/';
|
||||||
|
var CACHE_DIR = path.join(settings.root, 'var');
|
||||||
|
=======
|
||||||
|
var ROOT_DIR = path.normalize(__dirname + "/../../static/");
|
||||||
|
>>>>>>> pita
|
||||||
|
var TAR_PATH = path.join(__dirname, 'tar.json');
|
||||||
|
var tar = JSON.parse(fs.readFileSync(TAR_PATH, 'utf8'));
|
||||||
|
|
||||||
|
// Rewrite tar to include modules with no extensions and proper rooted paths.
|
||||||
|
exports.tar = {};
|
||||||
|
for (var key in tar) {
|
||||||
|
exports.tar['/' + key] =
|
||||||
|
tar[key].map(function (p) {return '/' + p}).concat(
|
||||||
|
tar[key].map(function (p) {return '/' + p.replace(/\.js$/, '')})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* creates the minifed javascript for the given minified name
|
||||||
|
* @param req the Express request
|
||||||
|
* @param res the Express response
|
||||||
|
*/
|
||||||
|
exports.minify = function(req, res, next)
|
||||||
|
{
|
||||||
|
<<<<<<< HEAD
|
||||||
|
var jsFilename = req.params[0];
|
||||||
|
|
||||||
|
//choose the js files we need
|
||||||
|
var jsFiles = undefined;
|
||||||
|
if (Object.prototype.hasOwnProperty.call(tar, jsFilename)) {
|
||||||
|
jsFiles = tar[jsFilename];
|
||||||
|
} else {
|
||||||
|
/* Not in tar list, but try anyways, if it fails, pass to `next`.
|
||||||
|
Actually try, not check in filesystem here because
|
||||||
|
we don't want to duplicate the require.resolve() handling
|
||||||
|
*/
|
||||||
|
jsFiles = [jsFilename];
|
||||||
|
}
|
||||||
|
_handle(req, res, jsFilename, jsFiles, function (err) {
|
||||||
|
console.log("Unable to load minified file " + jsFilename + ": " + err.toString());
|
||||||
|
/* Throw away error and generate a 404, not 500 */
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function _handle(req, res, jsFilename, jsFiles, next) {
|
||||||
|
res.header("Content-Type","text/javascript");
|
||||||
|
|
||||||
|
var cacheName = CACHE_DIR + "/minified_" + jsFilename.replace(/\//g, "_");
|
||||||
|
|
||||||
|
//minifying is enabled
|
||||||
|
if(settings.minify)
|
||||||
|
{
|
||||||
|
var result = undefined;
|
||||||
|
var latestModification = 0;
|
||||||
|
|
||||||
|
async.series([
|
||||||
|
//find out the highest modification date
|
||||||
|
function(callback)
|
||||||
|
{
|
||||||
|
var folders2check = [CSS_DIR, JS_DIR];
|
||||||
|
|
||||||
|
//go trough this two folders
|
||||||
|
async.forEach(folders2check, function(path, callback)
|
||||||
|
{
|
||||||
|
//read the files in the folder
|
||||||
|
fs.readdir(path, function(err, files)
|
||||||
|
{
|
||||||
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
|
//we wanna check the directory itself for changes too
|
||||||
|
files.push(".");
|
||||||
|
|
||||||
|
//go trough all files in this folder
|
||||||
|
async.forEach(files, function(filename, callback)
|
||||||
|
{
|
||||||
|
//get the stat data of this file
|
||||||
|
fs.stat(path + "/" + filename, function(err, stats)
|
||||||
|
{
|
||||||
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
|
//get the modification time
|
||||||
|
var modificationTime = stats.mtime.getTime();
|
||||||
|
|
||||||
|
//compare the modification time to the highest found
|
||||||
|
if(modificationTime > latestModification)
|
||||||
|
{
|
||||||
|
latestModification = modificationTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
}, callback);
|
||||||
|
});
|
||||||
|
}, callback);
|
||||||
|
},
|
||||||
|
function(callback)
|
||||||
|
{
|
||||||
|
//check the modification time of the minified js
|
||||||
|
fs.stat(cacheName, function(err, stats)
|
||||||
|
{
|
||||||
|
if(err && err.code != "ENOENT")
|
||||||
|
{
|
||||||
|
ERR(err, callback);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//there is no minfied file or there new changes since this file was generated, so continue generating this file
|
||||||
|
if((err && err.code == "ENOENT") || stats.mtime.getTime() < latestModification)
|
||||||
|
{
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
//the minified file is still up to date, stop minifying
|
||||||
|
else
|
||||||
|
{
|
||||||
|
callback("stop");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
//load all js files
|
||||||
|
function (callback)
|
||||||
|
{
|
||||||
|
var values = [];
|
||||||
|
tarCode(
|
||||||
|
jsFiles
|
||||||
|
, function (content) {values.push(content)}
|
||||||
|
, function (err) {
|
||||||
|
if(ERR(err, next)) return;
|
||||||
|
|
||||||
|
result = values.join('');
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
//put all together and write it into a file
|
||||||
|
function(callback)
|
||||||
|
{
|
||||||
|
async.parallel([
|
||||||
|
//write the results plain in a file
|
||||||
|
function(callback)
|
||||||
|
{
|
||||||
|
fs.writeFile(cacheName, result, "utf8", callback);
|
||||||
|
},
|
||||||
|
//write the results compressed in a file
|
||||||
|
function(callback)
|
||||||
|
{
|
||||||
|
zlib.gzip(result, function(err, compressedResult){
|
||||||
|
//weird gzip bug that returns 0 instead of null if everything is ok
|
||||||
|
err = err === 0 ? null : err;
|
||||||
|
|
||||||
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
|
fs.writeFile(cacheName + ".gz", compressedResult, callback);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
],callback);
|
||||||
|
}
|
||||||
|
], function(err)
|
||||||
|
{
|
||||||
|
if(err && err != "stop")
|
||||||
|
{
|
||||||
|
if(ERR(err)) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//check if gzip is supported by this browser
|
||||||
|
var gzipSupport = req.header('Accept-Encoding', '').indexOf('gzip') != -1;
|
||||||
|
|
||||||
|
var pathStr;
|
||||||
|
if(gzipSupport && os.type().indexOf("Windows") == -1)
|
||||||
|
{
|
||||||
|
pathStr = path.normalize(cacheName + ".gz");
|
||||||
|
res.header('Content-Encoding', 'gzip');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pathStr = path.normalize(cacheName);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.sendfile(pathStr, { maxAge: server.maxAge });
|
||||||
|
})
|
||||||
|
}
|
||||||
|
//minifying is disabled, so put the files together in one file
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tarCode(
|
||||||
|
jsFiles
|
||||||
|
, function (content) {res.write(content)}
|
||||||
|
, function (err) {
|
||||||
|
if(ERR(err, next)) return;
|
||||||
|
=======
|
||||||
|
var filename = req.params['filename'];
|
||||||
|
|
||||||
|
// No relative paths, especially if they may go up the file hierarchy.
|
||||||
|
filename = path.normalize(path.join(ROOT_DIR, filename));
|
||||||
|
if (filename.indexOf(ROOT_DIR) == 0) {
|
||||||
|
filename = filename.slice(ROOT_DIR.length);
|
||||||
|
filename = filename.replace(/\\/g, '/'); // Windows (safe generally?)
|
||||||
|
} else {
|
||||||
|
res.writeHead(404, {});
|
||||||
|
res.end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// What content type should this be?
|
||||||
|
// TODO: This should use a MIME module.
|
||||||
|
var contentType;
|
||||||
|
if (filename.match(/\.js$/)) {
|
||||||
|
contentType = "text/javascript";
|
||||||
|
} else if (filename.match(/\.css$/)) {
|
||||||
|
contentType = "text/css";
|
||||||
|
} else if (filename.match(/\.html$/)) {
|
||||||
|
contentType = "text/html";
|
||||||
|
} else if (filename.match(/\.txt$/)) {
|
||||||
|
contentType = "text/plain";
|
||||||
|
} else if (filename.match(/\.png$/)) {
|
||||||
|
contentType = "image/png";
|
||||||
|
} else if (filename.match(/\.gif$/)) {
|
||||||
|
contentType = "image/gif";
|
||||||
|
} else if (filename.match(/\.ico$/)) {
|
||||||
|
contentType = "image/x-icon";
|
||||||
|
} else {
|
||||||
|
contentType = "application/octet-stream";
|
||||||
|
}
|
||||||
|
|
||||||
|
statFile(filename, function (error, date, exists) {
|
||||||
|
if (date) {
|
||||||
|
date = new Date(date);
|
||||||
|
res.setHeader('last-modified', date.toUTCString());
|
||||||
|
res.setHeader('date', (new Date()).toUTCString());
|
||||||
|
if (server.maxAge) {
|
||||||
|
var expiresDate = new Date((new Date()).getTime()+server.maxAge*1000);
|
||||||
|
res.setHeader('expires', expiresDate.toUTCString());
|
||||||
|
res.setHeader('cache-control', 'max-age=' + server.maxAge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
res.writeHead(500, {});
|
||||||
|
>>>>>>> pita
|
||||||
|
res.end();
|
||||||
|
} else if (!exists) {
|
||||||
|
res.writeHead(404, {});
|
||||||
|
res.end();
|
||||||
|
} else if (new Date(req.headers['if-modified-since']) >= date) {
|
||||||
|
res.writeHead(304, {});
|
||||||
|
res.end();
|
||||||
|
} else {
|
||||||
|
if (req.method == 'HEAD') {
|
||||||
|
res.header("Content-Type", contentType);
|
||||||
|
res.writeHead(200, {});
|
||||||
|
res.end();
|
||||||
|
} else if (req.method == 'GET') {
|
||||||
|
getFileCompressed(filename, contentType, function (error, content) {
|
||||||
|
if(ERR(error)) return;
|
||||||
|
res.header("Content-Type", contentType);
|
||||||
|
res.writeHead(200, {});
|
||||||
|
res.write(content);
|
||||||
|
res.end();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.writeHead(405, {'allow': 'HEAD, GET'});
|
||||||
|
res.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// find all includes in ace.js and embed them.
|
||||||
|
function getAceFile(callback) {
|
||||||
|
fs.readFile(ROOT_DIR + 'js/ace.js', "utf8", function(err, data) {
|
||||||
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
|
// Find all includes in ace.js and embed them
|
||||||
|
var founds = data.match(/\$\$INCLUDE_[a-zA-Z_]+\("[^"]*"\)/gi);
|
||||||
|
if (!settings.minify) {
|
||||||
|
founds = [];
|
||||||
|
}
|
||||||
|
// Always include the require kernel.
|
||||||
|
founds.push('$$INCLUDE_JS("../static/js/require-kernel.js")');
|
||||||
|
|
||||||
|
data += ';\n';
|
||||||
|
data += 'Ace2Editor.EMBEDED = Ace2Editor.EMBEDED || {};\n';
|
||||||
|
|
||||||
|
// Request the contents of the included file on the server-side and write
|
||||||
|
// them into the file.
|
||||||
|
async.forEach(founds, function (item, callback) {
|
||||||
|
var filename = item.match(/"([^"]*)"/)[1];
|
||||||
|
var request = require('request');
|
||||||
|
|
||||||
|
var baseURI = 'http://localhost:' + settings.port
|
||||||
|
|
||||||
|
request(baseURI + path.normalize(path.join('/static/', filename)), function (error, response, body) {
|
||||||
|
if (!error && response.statusCode == 200) {
|
||||||
|
data += 'Ace2Editor.EMBEDED[' + JSON.stringify(filename) + '] = '
|
||||||
|
+ JSON.stringify(body || '') + ';\n';
|
||||||
|
} else {
|
||||||
|
// Silence?
|
||||||
|
}
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
}, function(error) {
|
||||||
|
callback(error, data);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for the existance of the file and get the last modification date.
|
||||||
|
function statFile(filename, callback) {
|
||||||
|
if (filename == 'js/ace.js') {
|
||||||
|
// Sometimes static assets are inlined into this file, so we have to stat
|
||||||
|
// everything.
|
||||||
|
lastModifiedDateOfEverything(function (error, date) {
|
||||||
|
callback(error, date, !error);
|
||||||
|
});
|
||||||
|
} else if (filename == 'js/require-kernel.js') {
|
||||||
|
callback(null, requireLastModified(), true);
|
||||||
|
} else {
|
||||||
|
fs.stat(ROOT_DIR + filename, function (error, stats) {
|
||||||
|
if (error) {
|
||||||
|
if (error.code == "ENOENT") {
|
||||||
|
// Stat the directory instead.
|
||||||
|
fs.stat(path.dirname(ROOT_DIR + filename), function (error, stats) {
|
||||||
|
if (error) {
|
||||||
|
if (error.code == "ENOENT") {
|
||||||
|
callback(null, null, false);
|
||||||
|
} else {
|
||||||
|
callback(error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
callback(null, stats.mtime.getTime(), false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
callback(error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
callback(null, stats.mtime.getTime(), true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function lastModifiedDateOfEverything(callback) {
|
||||||
|
var folders2check = [ROOT_DIR + 'js/', ROOT_DIR + 'css/'];
|
||||||
|
var latestModification = 0;
|
||||||
|
//go trough this two folders
|
||||||
|
async.forEach(folders2check, function(path, callback)
|
||||||
|
{
|
||||||
|
//read the files in the folder
|
||||||
|
fs.readdir(path, function(err, files)
|
||||||
|
{
|
||||||
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
|
//we wanna check the directory itself for changes too
|
||||||
|
files.push(".");
|
||||||
|
|
||||||
|
//go trough all files in this folder
|
||||||
|
async.forEach(files, function(filename, callback)
|
||||||
|
{
|
||||||
|
//get the stat data of this file
|
||||||
|
fs.stat(path + "/" + filename, function(err, stats)
|
||||||
|
{
|
||||||
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
|
//get the modification time
|
||||||
|
var modificationTime = stats.mtime.getTime();
|
||||||
|
|
||||||
|
//compare the modification time to the highest found
|
||||||
|
if(modificationTime > latestModification)
|
||||||
|
{
|
||||||
|
latestModification = modificationTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
}, callback);
|
||||||
|
});
|
||||||
|
}, function () {
|
||||||
|
callback(null, latestModification);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// This should be provided by the module, but until then, just use startup
|
||||||
|
// time.
|
||||||
|
var _requireLastModified = new Date();
|
||||||
|
function requireLastModified() {
|
||||||
|
return _requireLastModified.toUTCString();
|
||||||
|
}
|
||||||
|
function requireDefinition() {
|
||||||
|
return 'var require = ' + RequireKernel.kernelSource + ';\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
|
function tarCode(jsFiles, write, callback) {
|
||||||
|
write('require.define({');
|
||||||
|
var initialEntry = true;
|
||||||
|
async.forEach(jsFiles, function (filename, callback){
|
||||||
|
var path;
|
||||||
|
var srcPath;
|
||||||
|
if (filename.indexOf('plugins/') == 0) {
|
||||||
|
srcPath = filename.substring('plugins/'.length);
|
||||||
|
path = require.resolve(srcPath);
|
||||||
|
} else {
|
||||||
|
srcPath = '/' + filename;
|
||||||
|
path = JS_DIR + filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
srcPath = JSON.stringify(srcPath);
|
||||||
|
var srcPathAbbv = JSON.stringify(srcPath.replace(/\.js$/, ''));
|
||||||
|
|
||||||
|
if (filename == 'ace.js') {
|
||||||
|
getAceFile(handleFile);
|
||||||
|
} else {
|
||||||
|
fs.readFile(path, "utf8", handleFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleFile(err, data) {
|
||||||
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
|
if (!initialEntry) {
|
||||||
|
write('\n,');
|
||||||
|
} else {
|
||||||
|
initialEntry = false;
|
||||||
|
}
|
||||||
|
write(srcPath + ': ')
|
||||||
|
data = '(function (require, exports, module) {' + data + '})';
|
||||||
|
=======
|
||||||
|
function getFileCompressed(filename, contentType, callback) {
|
||||||
|
getFile(filename, function (error, content) {
|
||||||
|
if (error || !content) {
|
||||||
|
callback(error, content);
|
||||||
|
} else {
|
||||||
|
>>>>>>> pita
|
||||||
|
if (settings.minify) {
|
||||||
|
if (contentType == 'text/javascript') {
|
||||||
|
try {
|
||||||
|
content = compressJS([content]);
|
||||||
|
} catch (error) {
|
||||||
|
// silence
|
||||||
|
}
|
||||||
|
} else if (contentType == 'text/css') {
|
||||||
|
content = compressCSS([content]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
callback(null, content);
|
||||||
|
}
|
||||||
|
<<<<<<< HEAD
|
||||||
|
}, function (err) {
|
||||||
|
if(ERR(err, callback)) return;
|
||||||
|
write('});\n');
|
||||||
|
callback();
|
||||||
|
=======
|
||||||
|
>>>>>>> pita
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFile(filename, callback) {
|
||||||
|
if (filename == 'js/ace.js') {
|
||||||
|
getAceFile(callback);
|
||||||
|
} else if (filename == 'js/require-kernel.js') {
|
||||||
|
callback(undefined, requireDefinition());
|
||||||
|
} else {
|
||||||
|
fs.readFile(ROOT_DIR + filename, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function compressJS(values)
|
||||||
|
{
|
||||||
|
var complete = values.join("\n");
|
||||||
|
var ast = jsp.parse(complete); // parse code and get the initial AST
|
||||||
|
ast = pro.ast_mangle(ast); // get a new AST with mangled names
|
||||||
|
ast = pro.ast_squeeze(ast); // get an AST with compression optimizations
|
||||||
|
return pro.gen_code(ast); // compressed code here
|
||||||
|
}
|
||||||
|
|
||||||
|
function compressCSS(values)
|
||||||
|
{
|
||||||
|
var complete = values.join("\n");
|
||||||
|
return cleanCSS.process(complete);
|
||||||
|
}
|
|
@ -23,6 +23,10 @@ var fs = require("fs");
|
||||||
var os = require("os");
|
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");
|
||||||
|
|
||||||
|
/* Root path of the installation */
|
||||||
|
exports.root = path.normalize(path.join(npm.dir, ".."));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The IP ep-lite should listen to
|
* The IP ep-lite should listen to
|
||||||
|
@ -40,7 +44,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" : "../var/dirty.db" };
|
exports.dbSettings = { "filename" : path.join(exports.root, "var/dirty.db") };
|
||||||
/**
|
/**
|
||||||
* The default Text of a new pad
|
* The default Text of a new pad
|
||||||
*/
|
*/
|
||||||
|
@ -81,6 +85,11 @@ exports.loglevel = "INFO";
|
||||||
*/
|
*/
|
||||||
exports.httpAuth = null;
|
exports.httpAuth = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Http basic auth, with "user:password" format
|
||||||
|
*/
|
||||||
|
exports.adminHttpAuth = null;
|
||||||
|
|
||||||
//checks if abiword is avaiable
|
//checks if abiword is avaiable
|
||||||
exports.abiwordAvailable = function()
|
exports.abiwordAvailable = function()
|
||||||
{
|
{
|
||||||
|
@ -96,10 +105,12 @@ 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";
|
||||||
var settingsPath = settingsFilename.charAt(0) == '/' ? '' : path.normalize(__dirname + "/../../");
|
if (settingsFilename.charAt(0) != '/') {
|
||||||
|
settingsFilename = path.normalize(path.join(root, settingsFilename));
|
||||||
|
}
|
||||||
|
|
||||||
//read the settings sync
|
//read the settings sync
|
||||||
var settingsStr = fs.readFileSync(settingsPath + settingsFilename).toString();
|
var settingsStr = fs.readFileSync(settingsFilename).toString();
|
||||||
|
|
||||||
//remove all comments
|
//remove all comments
|
||||||
settingsStr = settingsStr.replace(/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/gm,"").replace(/#.*/g,"").replace(/\/\/.*/g,"");
|
settingsStr = settingsStr.replace(/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/gm,"").replace(/#.*/g,"").replace(/\/\/.*/g,"");
|
|
@ -18,12 +18,12 @@ var async = require('async');
|
||||||
var Buffer = require('buffer').Buffer;
|
var Buffer = require('buffer').Buffer;
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var server = require('../server');
|
|
||||||
var zlib = require('zlib');
|
var zlib = require('zlib');
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
|
var settings = require('./Settings');
|
||||||
|
|
||||||
var ROOT_DIR = path.normalize(__dirname + "/../");
|
var CACHE_DIR = path.normalize(path.join(settings.root, 'var/'));
|
||||||
var CACHE_DIR = ROOT_DIR + '../var/';
|
CACHE_DIR = path.existsSync(CACHE_DIR) ? CACHE_DIR : undefined;
|
||||||
|
|
||||||
var responseCache = {};
|
var responseCache = {};
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ function CachingMiddleware() {
|
||||||
}
|
}
|
||||||
CachingMiddleware.prototype = new function () {
|
CachingMiddleware.prototype = new function () {
|
||||||
function handle(req, res, next) {
|
function handle(req, res, next) {
|
||||||
if (!(req.method == "GET" || req.method == "HEAD")) {
|
if (!(req.method == "GET" || req.method == "HEAD") || !CACHE_DIR) {
|
||||||
return next(undefined, req, res);
|
return next(undefined, req, res);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
/**
|
||||||
|
* Generates a random String with the given length. Is needed to generate the Author, Group, readonly, session Ids
|
||||||
|
*/
|
||||||
|
var randomString = function randomString(len)
|
||||||
|
{
|
||||||
|
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||||
|
var randomstring = '';
|
||||||
|
for (var i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
var rnum = Math.floor(Math.random() * chars.length);
|
||||||
|
randomstring += chars.substring(rnum, rnum + 1);
|
||||||
|
}
|
||||||
|
return randomstring;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = randomString;
|
|
@ -1,11 +1,11 @@
|
||||||
{
|
{
|
||||||
"pad.js": [
|
"pad.js": [
|
||||||
"jquery.js"
|
"jquery.js"
|
||||||
|
, "underscore.js"
|
||||||
, "security.js"
|
, "security.js"
|
||||||
, "pad.js"
|
, "pad.js"
|
||||||
, "ace2_common.js"
|
, "ace2_common.js"
|
||||||
, "pad_utils.js"
|
, "pad_utils.js"
|
||||||
, "plugins.js"
|
|
||||||
, "undo-xpopup.js"
|
, "undo-xpopup.js"
|
||||||
, "json2.js"
|
, "json2.js"
|
||||||
, "pad_cookie.js"
|
, "pad_cookie.js"
|
||||||
|
@ -22,12 +22,11 @@
|
||||||
, "chat.js"
|
, "chat.js"
|
||||||
, "excanvas.js"
|
, "excanvas.js"
|
||||||
, "farbtastic.js"
|
, "farbtastic.js"
|
||||||
, "prefixfree.js"
|
|
||||||
]
|
]
|
||||||
, "timeslider.js": [
|
, "timeslider.js": [
|
||||||
"jquery.js"
|
"jquery.js"
|
||||||
|
, "underscore.js"
|
||||||
, "security.js"
|
, "security.js"
|
||||||
, "plugins.js"
|
|
||||||
, "undo-xpopup.js"
|
, "undo-xpopup.js"
|
||||||
, "json2.js"
|
, "json2.js"
|
||||||
, "colorutils.js"
|
, "colorutils.js"
|
||||||
|
@ -41,7 +40,7 @@
|
||||||
, "pad_modals.js"
|
, "pad_modals.js"
|
||||||
, "pad_savedrevs.js"
|
, "pad_savedrevs.js"
|
||||||
, "pad_impexp.js"
|
, "pad_impexp.js"
|
||||||
, "AttributePoolFactory.js"
|
, "AttributePool.js"
|
||||||
, "Changeset.js"
|
, "Changeset.js"
|
||||||
, "domline.js"
|
, "domline.js"
|
||||||
, "linestylefilter.js"
|
, "linestylefilter.js"
|
||||||
|
@ -53,8 +52,11 @@
|
||||||
]
|
]
|
||||||
, "ace2_inner.js": [
|
, "ace2_inner.js": [
|
||||||
"ace2_common.js"
|
"ace2_common.js"
|
||||||
, "AttributePoolFactory.js"
|
, "underscore.js"
|
||||||
|
, "rjquery.js"
|
||||||
|
, "AttributePool.js"
|
||||||
, "Changeset.js"
|
, "Changeset.js"
|
||||||
|
, "ChangesetUtils.js"
|
||||||
, "security.js"
|
, "security.js"
|
||||||
, "skiplist.js"
|
, "skiplist.js"
|
||||||
, "virtual_lines.js"
|
, "virtual_lines.js"
|
||||||
|
@ -65,6 +67,7 @@
|
||||||
, "changesettracker.js"
|
, "changesettracker.js"
|
||||||
, "linestylefilter.js"
|
, "linestylefilter.js"
|
||||||
, "domline.js"
|
, "domline.js"
|
||||||
|
, "AttributeManager.js"
|
||||||
, "ace2_inner.js"
|
, "ace2_inner.js"
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"name" : "etherpad-lite",
|
"name" : "ep_etherpad-lite",
|
||||||
"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"],
|
||||||
|
@ -12,7 +12,7 @@
|
||||||
"dependencies" : {
|
"dependencies" : {
|
||||||
"yajsml" : "1.1.2",
|
"yajsml" : "1.1.2",
|
||||||
"request" : "2.9.100",
|
"request" : "2.9.100",
|
||||||
"require-kernel" : "1.0.3",
|
"require-kernel" : "1.0.5",
|
||||||
"socket.io" : "0.8.7",
|
"socket.io" : "0.8.7",
|
||||||
"ueberDB" : "0.1.7",
|
"ueberDB" : "0.1.7",
|
||||||
"async" : "0.1.18",
|
"async" : "0.1.18",
|
||||||
|
@ -22,7 +22,13 @@
|
||||||
"formidable" : "1.0.9",
|
"formidable" : "1.0.9",
|
||||||
"log4js" : "0.4.1",
|
"log4js" : "0.4.1",
|
||||||
"jsdom-nocontextifiy" : "0.2.10",
|
"jsdom-nocontextifiy" : "0.2.10",
|
||||||
"async-stacktrace" : "0.0.2"
|
"async-stacktrace" : "0.0.2",
|
||||||
|
"npm" : "1.1",
|
||||||
|
"ejs" : "0.6.1",
|
||||||
|
"graceful-fs" : "1.1.5",
|
||||||
|
"slide" : "1.1.3",
|
||||||
|
"semver" : "1.0.13",
|
||||||
|
"underscore" : "1.3.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"jshint" : "*"
|
"jshint" : "*"
|
|
@ -0,0 +1,49 @@
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
td, th {
|
||||||
|
border: 1px solid black;
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
padding-top: 2px;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
}
|
||||||
|
.template {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.dialog {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
width: 700px;
|
||||||
|
height: 500px;
|
||||||
|
margin-left: -350px;
|
||||||
|
margin-top: -250px;
|
||||||
|
border: 3px solid #999999;
|
||||||
|
background: #eeeeee;
|
||||||
|
}
|
||||||
|
.dialog .title {
|
||||||
|
margin: 0;
|
||||||
|
padding: 2px;
|
||||||
|
border-bottom: 3px solid #999999;
|
||||||
|
font-size: 24px;
|
||||||
|
line-height: 24px;
|
||||||
|
height: 24px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.dialog .title .close {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
.dialog .history {
|
||||||
|
background: #222222;
|
||||||
|
color: #eeeeee;
|
||||||
|
position: absolute;
|
||||||
|
top: 41px;
|
||||||
|
bottom: 10px;
|
||||||
|
left: 10px;
|
||||||
|
right: 10px;
|
||||||
|
padding: 2px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ a img
|
||||||
}
|
}
|
||||||
|
|
||||||
/* menu */
|
/* menu */
|
||||||
#editbar ul
|
.toolbar ul
|
||||||
{
|
{
|
||||||
position: relative;
|
position: relative;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
|
@ -35,18 +35,19 @@ a img
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#editbar
|
.toolbar
|
||||||
{
|
{
|
||||||
background: #f7f7f7;
|
background: #f7f7f7;
|
||||||
background: linear-gradient(#f7f7f7, #f1f1f1 80%);
|
background: linear-gradient(#f7f7f7, #f1f1f1 80%);
|
||||||
border-bottom: 1px solid #ccc;
|
border-bottom: 1px solid #ccc;
|
||||||
height: 32px;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding-top: 3px;
|
padding-top: 3px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
white-space: nowrap;
|
||||||
|
height: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#editbar ul li
|
.toolbar ul li
|
||||||
{
|
{
|
||||||
background: #fff;
|
background: #fff;
|
||||||
background: linear-gradient(#fff, #f0f0f0);
|
background: linear-gradient(#fff, #f0f0f0);
|
||||||
|
@ -61,52 +62,52 @@ a img
|
||||||
width: 18px;
|
width: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#editbar ul li a
|
.toolbar ul li a
|
||||||
{
|
{
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: #ccc;
|
color: #ccc;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
|
||||||
#editbar ul li a span
|
.toolbar ul li a span
|
||||||
{
|
{
|
||||||
position: relative;
|
position: relative;
|
||||||
top:-2px
|
top:-2px
|
||||||
}
|
}
|
||||||
|
|
||||||
#editbar ul li:hover {
|
.toolbar ul li:hover {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
background: linear-gradient(#f4f4f4, #e4e4e4);
|
background: linear-gradient(#f4f4f4, #e4e4e4);
|
||||||
}
|
}
|
||||||
|
|
||||||
#editbar ul li:active {
|
.toolbar ul li:active {
|
||||||
background: #eee;
|
background: #eee;
|
||||||
background: linear-gradient(#ddd, #fff);
|
background: linear-gradient(#ddd, #fff);
|
||||||
box-shadow: 0 0 8px rgba(0,0,0,.1) inset;
|
box-shadow: 0 0 8px rgba(0,0,0,.1) inset;
|
||||||
}
|
}
|
||||||
|
|
||||||
#editbar ul li.separator
|
.toolbar ul li.separator
|
||||||
{
|
{
|
||||||
border: inherit;
|
border: inherit;
|
||||||
background: inherit;
|
background: inherit;
|
||||||
visibility:hidden;
|
visibility:hidden;
|
||||||
width: 0px;
|
width: 0px;
|
||||||
}
|
}
|
||||||
#editbar ul li a
|
.toolbar ul li a
|
||||||
{
|
{
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
#editbar ul li a img
|
.toolbar ul li a img
|
||||||
{
|
{
|
||||||
padding: 1px;
|
padding: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#editbar ul
|
.toolbar ul
|
||||||
{
|
{
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
#editbar ul#menu_right
|
.toolbar ul.menu_right
|
||||||
{
|
{
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
@ -175,7 +176,6 @@ a#backtoprosite { padding-left: 20px; left: 6px;
|
||||||
background: url(static/img/protop.gif) no-repeat -5px -6px; }
|
background: url(static/img/protop.gif) no-repeat -5px -6px; }
|
||||||
#accountnav { right: 30px; color: #fff; }
|
#accountnav { right: 30px; color: #fff; }
|
||||||
|
|
||||||
.propad a#topbaretherpad { background: url(static/img/protop.gif) no-repeat -397px -3px; }
|
|
||||||
|
|
||||||
#specialkeyarea { top: 5px; left: 250px; color: yellow; font-weight: bold;
|
#specialkeyarea { top: 5px; left: 250px; color: yellow; font-weight: bold;
|
||||||
font-size: 1.5em; position: absolute; }
|
font-size: 1.5em; position: absolute; }
|
||||||
|
@ -320,7 +320,7 @@ a#hidetopmsg { position: absolute; right: 5px; bottom: 5px; }
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
#editbarsavetable
|
.toolbarsavetable
|
||||||
{
|
{
|
||||||
position:absolute;
|
position:absolute;
|
||||||
top: 6px;
|
top: 6px;
|
||||||
|
@ -328,7 +328,7 @@ a#hidetopmsg { position: absolute; right: 5px; bottom: 5px; }
|
||||||
height: 24px;
|
height: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#editbarsavetable td, #editbartable td
|
.toolbarsavetable td, .toolbartable td
|
||||||
{
|
{
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
@ -604,8 +604,6 @@ table#otheruserstable { display: none; }
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nonprouser #sharebox-stripe { display: none; }
|
|
||||||
|
|
||||||
.sharebox-url {
|
.sharebox-url {
|
||||||
width: 440px; height: 18px;
|
width: 440px; height: 18px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
@ -688,14 +686,15 @@ a#topbarmaximize {
|
||||||
background: url(static/img/maximize_maximized.png);
|
background: url(static/img/maximize_maximized.png);
|
||||||
}
|
}
|
||||||
|
|
||||||
#editbarinner h1 {
|
.toolbarinner h1 {
|
||||||
line-height: 29px;
|
line-height: 29px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
padding-left: 6pt;
|
padding-left: 6pt;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
#editbarinner h1 a {
|
.toolbarinner h1 a {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1034,6 +1033,9 @@ margin-top: 1px;
|
||||||
background-position: 0px -183px;
|
background-position: 0px -183px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
.buttonicon-savedRevision {
|
||||||
|
background-position: 0px -493px
|
||||||
|
}
|
||||||
|
|
||||||
#usericon
|
#usericon
|
||||||
{
|
{
|
||||||
|
@ -1173,13 +1175,13 @@ input[type=checkbox] {
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 600px) {
|
@media screen and (max-width: 600px) {
|
||||||
#editbar ul li {
|
.toolbar ul li {
|
||||||
padding: 4px 1px;
|
padding: 4px 1px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (min-device-width: 320px) and (max-device-width: 720px) {
|
@media only screen and (min-device-width: 320px) and (max-device-width: 720px) {
|
||||||
#editbar ul li {
|
.toolbar ul li {
|
||||||
padding: 4px 3px;
|
padding: 4px 3px;
|
||||||
}
|
}
|
||||||
#users {
|
#users {
|
||||||
|
@ -1194,7 +1196,7 @@ input[type=checkbox] {
|
||||||
#editorcontainer {
|
#editorcontainer {
|
||||||
margin-bottom: 33px;
|
margin-bottom: 33px;
|
||||||
}
|
}
|
||||||
#editbar ul#menu_right {
|
.toolbar ul.menu_right {
|
||||||
background: #f7f7f7;
|
background: #f7f7f7;
|
||||||
background: linear-gradient(#f7f7f7, #f1f1f1 80%);
|
background: linear-gradient(#f7f7f7, #f1f1f1 80%);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -1204,7 +1206,7 @@ input[type=checkbox] {
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
border-top: 1px solid #ccc;
|
border-top: 1px solid #ccc;
|
||||||
}
|
}
|
||||||
#editbar ul#menu_right li:last-child {
|
.toolbar ul.menu_right li:last-child {
|
||||||
height: 24px;
|
height: 24px;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
|
@ -1226,7 +1228,7 @@ input[type=checkbox] {
|
||||||
border-top-right-radius: 0;
|
border-top-right-radius: 0;
|
||||||
border-right: none;
|
border-right: none;
|
||||||
}
|
}
|
||||||
#editbar ul li a span {
|
.toolbar ul li a span {
|
||||||
top: -3px;
|
top: -3px;
|
||||||
}
|
}
|
||||||
#usericonback {
|
#usericonback {
|
||||||
|
@ -1235,10 +1237,10 @@ input[type=checkbox] {
|
||||||
#qrcode {
|
#qrcode {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
#editbar ul#menu_right li:not(:last-child) {
|
.toolbar ul.menu_right li:not(:last-child) {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
#editbar ul#menu_right > li {
|
.toolbar ul.menu_right > li {
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
|
@ -1267,4 +1269,4 @@ input[type=checkbox] {
|
||||||
#online_count {
|
#online_count {
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,7 @@
|
||||||
#editorcontainerbox {overflow:auto; top:40px;}
|
#editorcontainerbox {
|
||||||
|
overflow:auto; top:40px;
|
||||||
|
position: static;
|
||||||
|
}
|
||||||
|
|
||||||
#padcontent {font-size:12px; padding:10px;}
|
#padcontent {font-size:12px; padding:10px;}
|
||||||
|
|
||||||
|
@ -42,10 +45,10 @@
|
||||||
|
|
||||||
#leftstar, #rightstar, #leftstep, #rightstep
|
#leftstar, #rightstar, #leftstep, #rightstep
|
||||||
{background:url(../../static/img/stepper_buttons.png) 0 0 no-repeat; height:21px; overflow:hidden; position:absolute;}
|
{background:url(../../static/img/stepper_buttons.png) 0 0 no-repeat; height:21px; overflow:hidden; position:absolute;}
|
||||||
#leftstar {background-position:0 44px; right:34px; top:8px; width:30px;}
|
#leftstar {background-position:0 -44px; right:34px; top:8px; width:30px;}
|
||||||
#rightstar {background-position:29px 44px; right:5px; top:8px; width:29px;}
|
#rightstar {background-position:-29px -44px; right:5px; top:8px; width:29px;}
|
||||||
#leftstep {background-position:0 22px; right:34px; top:20px; width:30px;}
|
#leftstep {background-position:0 -22px; right:34px; top:20px; width:30px;}
|
||||||
#rightstep {background-position:29px 22px; right:5px; top:20px; width:29px;}
|
#rightstep {background-position:-29px -22px; right:5px; top:20px; width:29px;}
|
||||||
|
|
||||||
#timeslider .star {
|
#timeslider .star {
|
||||||
background-image:url(../../static/img/star.png);
|
background-image:url(../../static/img/star.png);
|
||||||
|
@ -67,12 +70,63 @@
|
||||||
width:122px;
|
width:122px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.topbarcenter, #docbar {display:none;}
|
.topbarcenter, #docbar {display:none;}
|
||||||
#padmain {top:30px;}
|
#padmain {top:0px !important;}
|
||||||
#editbarright {float:right;}
|
#editbarright {float:right;}
|
||||||
#returnbutton {color:#222; font-size:16px; line-height:29px; margin-top:0; padding-right:6px;}
|
#returnbutton {color:#222; font-size:16px; line-height:29px; margin-top:0; padding-right:6px;}
|
||||||
#importexport {top:118px;}
|
|
||||||
#importexport .popup {width:185px;}
|
#importexport .popup {width:185px;}
|
||||||
|
#importexport{
|
||||||
|
top:118px;
|
||||||
|
width:185px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.timeslider-bar
|
||||||
|
{
|
||||||
|
background: #f7f7f7;
|
||||||
|
background: linear-gradient(#f7f7f7, #f1f1f1 80%);
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
|
overflow: hidden;
|
||||||
|
padding-top: 3px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeslider-bar #editbar
|
||||||
|
{
|
||||||
|
border-bottom: none;
|
||||||
|
float: right;
|
||||||
|
width: 170px;
|
||||||
|
width: initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeslider-bar h1
|
||||||
|
{
|
||||||
|
margin: 5px;
|
||||||
|
}
|
||||||
|
.timeslider-bar p
|
||||||
|
{
|
||||||
|
margin: 5px;
|
||||||
|
}
|
||||||
|
#timeslider-top {
|
||||||
|
width: 100%;
|
||||||
|
position: fixed;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#authorsList .author {
|
||||||
|
padding-left: 0.4em;
|
||||||
|
padding-right: 0.4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#authorsList .author-anonymous {
|
||||||
|
padding-left: 0.6em;
|
||||||
|
padding-right: 0.6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#padeditor {
|
||||||
|
position: static;
|
||||||
|
}
|
||||||
|
|
||||||
/* lists */
|
/* lists */
|
||||||
.list-bullet2, .list-indent2, .list-number2 {margin-left:3em;}
|
.list-bullet2, .list-indent2, .list-number2 {margin-left:3em;}
|
|
@ -0,0 +1,3 @@
|
||||||
|
*
|
||||||
|
!.gitignore
|
||||||
|
!*.template
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 697 B After Width: | Height: | Size: 697 B |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 1009 B After Width: | Height: | Size: 1009 B |
After Width: | Height: | Size: 8.1 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 494 B After Width: | Height: | Size: 494 B |
Before Width: | Height: | Size: 658 B After Width: | Height: | Size: 658 B |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 123 B After Width: | Height: | Size: 123 B |
Before Width: | Height: | Size: 131 B After Width: | Height: | Size: 131 B |
After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 182 B After Width: | Height: | Size: 182 B |
Before Width: | Height: | Size: 686 B After Width: | Height: | Size: 686 B |
Before Width: | Height: | Size: 517 B After Width: | Height: | Size: 517 B |
|
@ -0,0 +1,164 @@
|
||||||
|
var Changeset = require('./Changeset');
|
||||||
|
var ChangesetUtils = require('./ChangesetUtils');
|
||||||
|
var _ = require('./underscore');
|
||||||
|
|
||||||
|
var lineMarkerAttribute = 'lmkr';
|
||||||
|
|
||||||
|
// If one of these attributes are set to the first character of a
|
||||||
|
// line it is considered as a line attribute marker i.e. attributes
|
||||||
|
// set on this marker are applied to the whole line.
|
||||||
|
// The list attribute is only maintained for compatibility reasons
|
||||||
|
var lineAttributes = [lineMarkerAttribute,'list'];
|
||||||
|
|
||||||
|
/*
|
||||||
|
The Attribute manager builds changesets based on a document
|
||||||
|
representation for setting and removing range or line-based attributes.
|
||||||
|
|
||||||
|
@param rep the document representation to be used
|
||||||
|
@param applyChangesetCallback this callback will be called
|
||||||
|
once a changeset has been built.
|
||||||
|
|
||||||
|
|
||||||
|
A document representation contains
|
||||||
|
- an array `alines` containing 1 attributes string for each line
|
||||||
|
- an Attribute pool `apool`
|
||||||
|
- a SkipList `lines` containing the text lines of the document.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var AttributeManager = function(rep, applyChangesetCallback)
|
||||||
|
{
|
||||||
|
this.rep = rep;
|
||||||
|
this.applyChangesetCallback = applyChangesetCallback;
|
||||||
|
this.author = '';
|
||||||
|
|
||||||
|
// If the first char in a line has one of the following attributes
|
||||||
|
// it will be considered as a line marker
|
||||||
|
};
|
||||||
|
|
||||||
|
AttributeManager.lineAttributes = lineAttributes;
|
||||||
|
|
||||||
|
AttributeManager.prototype = _(AttributeManager.prototype).extend({
|
||||||
|
|
||||||
|
applyChangeset: function(changeset){
|
||||||
|
if(!this.applyChangesetCallback) return changeset;
|
||||||
|
|
||||||
|
var cs = changeset.toString();
|
||||||
|
if (!Changeset.isIdentity(cs))
|
||||||
|
{
|
||||||
|
this.applyChangesetCallback(cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return changeset;
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
Sets attributes on a range
|
||||||
|
@param start [row, col] tuple pointing to the start of the range
|
||||||
|
@param end [row, col] tuple pointing to the end of the range
|
||||||
|
@param attribute: an array of attributes
|
||||||
|
*/
|
||||||
|
setAttributesOnRange: function(start, end, attribs)
|
||||||
|
{
|
||||||
|
var builder = Changeset.builder(this.rep.lines.totalWidth());
|
||||||
|
ChangesetUtils.buildKeepToStartOfRange(this.rep, builder, start);
|
||||||
|
ChangesetUtils.buildKeepRange(this.rep, builder, start, end, attribs, this.rep.apool);
|
||||||
|
return this.applyChangeset(builder);
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
Returns if the line already has a line marker
|
||||||
|
@param lineNum: the number of the line
|
||||||
|
*/
|
||||||
|
lineHasMarker: function(lineNum){
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
return _.find(lineAttributes, function(attribute){
|
||||||
|
return that.getAttributeOnLine(lineNum, attribute) != '';
|
||||||
|
}) !== undefined;
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
Gets a specified attribute on a line
|
||||||
|
@param lineNum: the number of the line to set the attribute for
|
||||||
|
@param attributeKey: the name of the attribute to get, e.g. list
|
||||||
|
*/
|
||||||
|
getAttributeOnLine: function(lineNum, attributeName){
|
||||||
|
// get `attributeName` attribute of first char of line
|
||||||
|
var aline = this.rep.alines[lineNum];
|
||||||
|
if (aline)
|
||||||
|
{
|
||||||
|
var opIter = Changeset.opIterator(aline);
|
||||||
|
if (opIter.hasNext())
|
||||||
|
{
|
||||||
|
return Changeset.opAttributeValue(opIter.next(), attributeName, this.rep.apool) || '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
Sets a specified attribute on a line
|
||||||
|
@param lineNum: the number of the line to set the attribute for
|
||||||
|
@param attributeKey: the name of the attribute to set, e.g. list
|
||||||
|
@param attributeValue: an optional parameter to pass to the attribute (e.g. indention level)
|
||||||
|
|
||||||
|
*/
|
||||||
|
setAttributeOnLine: function(lineNum, attributeName, attributeValue){
|
||||||
|
var loc = [0,0];
|
||||||
|
var builder = Changeset.builder(this.rep.lines.totalWidth());
|
||||||
|
var hasMarker = this.lineHasMarker(lineNum);
|
||||||
|
|
||||||
|
ChangesetUtils.buildKeepRange(this.rep, builder, loc, (loc = [lineNum, 0]));
|
||||||
|
|
||||||
|
if(hasMarker){
|
||||||
|
ChangesetUtils.buildKeepRange(this.rep, builder, loc, (loc = [lineNum, 1]), [
|
||||||
|
[attributeName, attributeValue]
|
||||||
|
], this.rep.apool);
|
||||||
|
}else{
|
||||||
|
// add a line marker
|
||||||
|
builder.insert('*', [
|
||||||
|
['author', this.author],
|
||||||
|
['insertorder', 'first'],
|
||||||
|
[lineMarkerAttribute, '1'],
|
||||||
|
[attributeName, attributeValue]
|
||||||
|
], this.rep.apool);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.applyChangeset(builder);
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
Removes a specified attribute on a line
|
||||||
|
@param lineNum: the number of the affected line
|
||||||
|
@param attributeKey: the name of the attribute to remove, e.g. list
|
||||||
|
|
||||||
|
*/
|
||||||
|
removeAttributeOnLine: function(lineNum, attributeName, attributeValue){
|
||||||
|
|
||||||
|
var loc = [0,0];
|
||||||
|
var builder = Changeset.builder(this.rep.lines.totalWidth());
|
||||||
|
var hasMarker = this.lineHasMarker(lineNum);
|
||||||
|
|
||||||
|
if(hasMarker){
|
||||||
|
ChangesetUtils.buildKeepRange(this.rep, builder, loc, (loc = [lineNum, 0]));
|
||||||
|
ChangesetUtils.buildRemoveRange(this.rep, builder, loc, (loc = [lineNum, 1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.applyChangeset(builder);
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
Sets a specified attribute on a line
|
||||||
|
@param lineNum: the number of the line to set the attribute for
|
||||||
|
@param attributeKey: the name of the attribute to set, e.g. list
|
||||||
|
@param attributeValue: an optional parameter to pass to the attribute (e.g. indention level)
|
||||||
|
*/
|
||||||
|
toggleAttributeOnLine: function(lineNum, attributeName, attributeValue) {
|
||||||
|
return this.getAttributeOnLine(attributeName) ?
|
||||||
|
this.removeAttributeOnLine(lineNum, attributeName) :
|
||||||
|
this.setAttributeOnLine(lineNum, attributeName, attributeValue);
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = AttributeManager;
|
|
@ -0,0 +1,96 @@
|
||||||
|
/**
|
||||||
|
* This code represents the Attribute Pool Object of the original Etherpad.
|
||||||
|
* 90% of the code is still like in the original Etherpad
|
||||||
|
* Look at https://github.com/ether/pad/blob/master/infrastructure/ace/www/easysync2.js
|
||||||
|
* You can find a explanation what a attribute pool is here:
|
||||||
|
* https://github.com/Pita/etherpad-lite/blob/master/doc/easysync/easysync-notes.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2009 Google Inc., 2011 Peter 'Pita' Martischka (Primary Technology Ltd)
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
An AttributePool maintains a mapping from [key,value] Pairs called
|
||||||
|
Attributes to Numbers (unsigened integers) and vice versa. These numbers are
|
||||||
|
used to reference Attributes in Changesets.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var AttributePool = function () {
|
||||||
|
this.numToAttrib = {}; // e.g. {0: ['foo','bar']}
|
||||||
|
this.attribToNum = {}; // e.g. {'foo,bar': 0}
|
||||||
|
this.nextNum = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
AttributePool.prototype.putAttrib = function (attrib, dontAddIfAbsent) {
|
||||||
|
var str = String(attrib);
|
||||||
|
if (str in this.attribToNum) {
|
||||||
|
return this.attribToNum[str];
|
||||||
|
}
|
||||||
|
if (dontAddIfAbsent) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
var num = this.nextNum++;
|
||||||
|
this.attribToNum[str] = num;
|
||||||
|
this.numToAttrib[num] = [String(attrib[0] || ''), String(attrib[1] || '')];
|
||||||
|
return num;
|
||||||
|
};
|
||||||
|
|
||||||
|
AttributePool.prototype.getAttrib = function (num) {
|
||||||
|
var pair = this.numToAttrib[num];
|
||||||
|
if (!pair) {
|
||||||
|
return pair;
|
||||||
|
}
|
||||||
|
return [pair[0], pair[1]]; // return a mutable copy
|
||||||
|
};
|
||||||
|
|
||||||
|
AttributePool.prototype.getAttribKey = function (num) {
|
||||||
|
var pair = this.numToAttrib[num];
|
||||||
|
if (!pair) return '';
|
||||||
|
return pair[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
AttributePool.prototype.getAttribValue = function (num) {
|
||||||
|
var pair = this.numToAttrib[num];
|
||||||
|
if (!pair) return '';
|
||||||
|
return pair[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
AttributePool.prototype.eachAttrib = function (func) {
|
||||||
|
for (var n in this.numToAttrib) {
|
||||||
|
var pair = this.numToAttrib[n];
|
||||||
|
func(pair[0], pair[1]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
AttributePool.prototype.toJsonable = function () {
|
||||||
|
return {
|
||||||
|
numToAttrib: this.numToAttrib,
|
||||||
|
nextNum: this.nextNum
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
AttributePool.prototype.fromJsonable = function (obj) {
|
||||||
|
this.numToAttrib = obj.numToAttrib;
|
||||||
|
this.nextNum = obj.nextNum;
|
||||||
|
this.attribToNum = {};
|
||||||
|
for (var n in this.numToAttrib) {
|
||||||
|
this.attribToNum[String(this.numToAttrib[n])] = Number(n);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = AttributePool;
|
|
@ -25,7 +25,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var AttributePoolFactory = require("/AttributePoolFactory");
|
var AttributePool = require("./AttributePool");
|
||||||
|
|
||||||
var _opt = null;
|
var _opt = null;
|
||||||
|
|
||||||
|
@ -1731,7 +1731,7 @@ exports.appendATextToAssembler = function (atext, assem) {
|
||||||
* @param pool {AtributePool}
|
* @param pool {AtributePool}
|
||||||
*/
|
*/
|
||||||
exports.prepareForWire = function (cs, pool) {
|
exports.prepareForWire = function (cs, pool) {
|
||||||
var newPool = AttributePoolFactory.createAttributePool();;
|
var newPool = new AttributePool();
|
||||||
var newCs = exports.moveOpsToNewPool(cs, pool, newPool);
|
var newCs = exports.moveOpsToNewPool(cs, pool, newPool);
|
||||||
return {
|
return {
|
||||||
translated: newCs,
|
translated: newCs,
|
|
@ -0,0 +1,60 @@
|
||||||
|
/**
|
||||||
|
* This module contains several helper Functions to build Changesets
|
||||||
|
* based on a SkipList
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
exports.buildRemoveRange = function(rep, builder, start, end)
|
||||||
|
{
|
||||||
|
var startLineOffset = rep.lines.offsetOfIndex(start[0]);
|
||||||
|
var endLineOffset = rep.lines.offsetOfIndex(end[0]);
|
||||||
|
|
||||||
|
if (end[0] > start[0])
|
||||||
|
{
|
||||||
|
builder.remove(endLineOffset - startLineOffset - start[1], end[0] - start[0]);
|
||||||
|
builder.remove(end[1]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
builder.remove(end[1] - start[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.buildKeepRange = function(rep, builder, start, end, attribs, pool)
|
||||||
|
{
|
||||||
|
var startLineOffset = rep.lines.offsetOfIndex(start[0]);
|
||||||
|
var endLineOffset = rep.lines.offsetOfIndex(end[0]);
|
||||||
|
|
||||||
|
if (end[0] > start[0])
|
||||||
|
{
|
||||||
|
builder.keep(endLineOffset - startLineOffset - start[1], end[0] - start[0], attribs, pool);
|
||||||
|
builder.keep(end[1], 0, attribs, pool);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
builder.keep(end[1] - start[1], 0, attribs, pool);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.buildKeepToStartOfRange = function(rep, builder, start)
|
||||||
|
{
|
||||||
|
var startLineOffset = rep.lines.offsetOfIndex(start[0]);
|
||||||
|
|
||||||
|
builder.keep(startLineOffset, start[0]);
|
||||||
|
builder.keep(start[1]);
|
||||||
|
}
|
||||||
|
|
|
@ -28,7 +28,8 @@ Ace2Editor.registry = {
|
||||||
nextId: 1
|
nextId: 1
|
||||||
};
|
};
|
||||||
|
|
||||||
var plugins = require('/plugins').plugins;
|
var hooks = require('./pluginfw/hooks');
|
||||||
|
var _ = require('./underscore');
|
||||||
|
|
||||||
function Ace2Editor()
|
function Ace2Editor()
|
||||||
{
|
{
|
||||||
|
@ -70,7 +71,7 @@ function Ace2Editor()
|
||||||
|
|
||||||
function doActionsPendingInit()
|
function doActionsPendingInit()
|
||||||
{
|
{
|
||||||
$.each(actionsPendingInit, function(i,fn){
|
_.each(actionsPendingInit, function(fn,i){
|
||||||
fn()
|
fn()
|
||||||
});
|
});
|
||||||
actionsPendingInit = [];
|
actionsPendingInit = [];
|
||||||
|
@ -87,7 +88,7 @@ function Ace2Editor()
|
||||||
'setUserChangeNotificationCallback', 'setAuthorInfo',
|
'setUserChangeNotificationCallback', 'setAuthorInfo',
|
||||||
'setAuthorSelectionRange', 'callWithAce', 'execCommand', 'replaceRange'];
|
'setAuthorSelectionRange', 'callWithAce', 'execCommand', 'replaceRange'];
|
||||||
|
|
||||||
$.each(aceFunctionsPendingInit, function(i,fnName){
|
_.each(aceFunctionsPendingInit, function(fnName,i){
|
||||||
var prefix = 'ace_';
|
var prefix = 'ace_';
|
||||||
var name = prefix + fnName;
|
var name = prefix + fnName;
|
||||||
editor[fnName] = pendingInit(function(){
|
editor[fnName] = pendingInit(function(){
|
||||||
|
@ -156,7 +157,11 @@ function Ace2Editor()
|
||||||
}
|
}
|
||||||
function pushRequireScriptTo(buffer) {
|
function pushRequireScriptTo(buffer) {
|
||||||
var KERNEL_SOURCE = '../static/js/require-kernel.js';
|
var KERNEL_SOURCE = '../static/js/require-kernel.js';
|
||||||
var KERNEL_BOOT = 'require.setRootURI("../minified/");\nrequire.setGlobalKeyPath("require");'
|
var KERNEL_BOOT = '\
|
||||||
|
require.setRootURI("../javascripts/src");\n\
|
||||||
|
require.setLibraryURI("../javascripts/lib");\n\
|
||||||
|
require.setGlobalKeyPath("require");\n\
|
||||||
|
';
|
||||||
if (Ace2Editor.EMBEDED && Ace2Editor.EMBEDED[KERNEL_SOURCE]) {
|
if (Ace2Editor.EMBEDED && Ace2Editor.EMBEDED[KERNEL_SOURCE]) {
|
||||||
buffer.push('<script type="text/javascript">');
|
buffer.push('<script type="text/javascript">');
|
||||||
buffer.push(Ace2Editor.EMBEDED[KERNEL_SOURCE]);
|
buffer.push(Ace2Editor.EMBEDED[KERNEL_SOURCE]);
|
||||||
|
@ -166,17 +171,18 @@ function Ace2Editor()
|
||||||
}
|
}
|
||||||
function pushScriptsTo(buffer) {
|
function pushScriptsTo(buffer) {
|
||||||
/* Folling is for packaging regular expression. */
|
/* Folling is for packaging regular expression. */
|
||||||
/* $$INCLUDE_JS("../minified/ace2_inner.js?callback=require.define"); */
|
/* $$INCLUDE_JS("../javascripts/src/ace2_inner.js?callback=require.define"); */
|
||||||
var ACE_SOURCE = '../minified/ace2_inner.js?callback=require.define';
|
var ACE_SOURCE = '../javascripts/src/ace2_inner.js?callback=require.define';
|
||||||
if (Ace2Editor.EMBEDED && Ace2Editor.EMBEDED[ACE_SOURCE]) {
|
if (Ace2Editor.EMBEDED && Ace2Editor.EMBEDED[ACE_SOURCE]) {
|
||||||
buffer.push('<script type="text/javascript">');
|
buffer.push('<script type="text/javascript">');
|
||||||
buffer.push(Ace2Editor.EMBEDED[ACE_SOURCE]);
|
buffer.push(Ace2Editor.EMBEDED[ACE_SOURCE]);
|
||||||
buffer.push('require("/ace2_inner");');
|
buffer.push('require("ep_etherpad-lite/static/js/ace2_inner");');
|
||||||
buffer.push('<\/script>');
|
buffer.push('<\/script>');
|
||||||
} else {
|
} else {
|
||||||
|
file = ACE_SOURCE;
|
||||||
buffer.push('<script type="application/javascript" src="' + ACE_SOURCE + '"><\/script>');
|
buffer.push('<script type="application/javascript" src="' + ACE_SOURCE + '"><\/script>');
|
||||||
buffer.push('<script type="text/javascript">');
|
buffer.push('<script type="text/javascript">');
|
||||||
buffer.push('require("/ace2_inner");');
|
buffer.push('require("ep_etherpad-lite/static/js/ace2_inner");');
|
||||||
buffer.push('<\/script>');
|
buffer.push('<\/script>');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -227,14 +233,16 @@ function Ace2Editor()
|
||||||
|
|
||||||
iframeHTML.push(doctype);
|
iframeHTML.push(doctype);
|
||||||
iframeHTML.push("<html><head>");
|
iframeHTML.push("<html><head>");
|
||||||
|
iframeHTML.push('<script type="text/javascript" src="../static/js/jquery.js"></script>');
|
||||||
|
|
||||||
|
hooks.callAll("aceInitInnerdocbodyHead", {
|
||||||
|
iframeHTML: iframeHTML
|
||||||
|
});
|
||||||
|
|
||||||
// For compatability's sake transform in and out.
|
// For compatability's sake transform in and out.
|
||||||
for (var i = 0, ii = iframeHTML.length; i < ii; i++) {
|
for (var i = 0, ii = iframeHTML.length; i < ii; i++) {
|
||||||
iframeHTML[i] = JSON.stringify(iframeHTML[i]);
|
iframeHTML[i] = JSON.stringify(iframeHTML[i]);
|
||||||
}
|
}
|
||||||
plugins.callHook("aceInitInnerdocbodyHead", {
|
|
||||||
iframeHTML: iframeHTML
|
|
||||||
});
|
|
||||||
for (var i = 0, ii = iframeHTML.length; i < ii; i++) {
|
for (var i = 0, ii = iframeHTML.length; i < ii; i++) {
|
||||||
iframeHTML[i] = JSON.parse(iframeHTML[i]);
|
iframeHTML[i] = JSON.parse(iframeHTML[i]);
|
||||||
}
|
}
|
||||||
|
@ -248,6 +256,10 @@ function Ace2Editor()
|
||||||
$$INCLUDE_CSS("../static/css/iframe_editor.css");
|
$$INCLUDE_CSS("../static/css/iframe_editor.css");
|
||||||
$$INCLUDE_CSS("../static/css/pad.css");
|
$$INCLUDE_CSS("../static/css/pad.css");
|
||||||
$$INCLUDE_CSS("../static/custom/pad.css");
|
$$INCLUDE_CSS("../static/custom/pad.css");
|
||||||
|
|
||||||
|
var additionalCSS = _(hooks.callAll("aceEditorCSS")).map(function(path){ return '../static/plugins/' + path });
|
||||||
|
includedCSS = includedCSS.concat(additionalCSS);
|
||||||
|
|
||||||
pushStyleTagsFor(iframeHTML, includedCSS);
|
pushStyleTagsFor(iframeHTML, includedCSS);
|
||||||
|
|
||||||
var includedJS = [];
|
var includedJS = [];
|
||||||
|
@ -256,9 +268,14 @@ function Ace2Editor()
|
||||||
// Inject my plugins into my child.
|
// Inject my plugins into my child.
|
||||||
iframeHTML.push('\
|
iframeHTML.push('\
|
||||||
<script type="text/javascript">\
|
<script type="text/javascript">\
|
||||||
|
parent_req = require("./pluginfw/parent_require.js");\
|
||||||
|
parent_req.getRequirementFromParent(require, "ep_etherpad-lite/static/js/pluginfw/hooks");\
|
||||||
|
parent_req.getRequirementFromParent(require, "ep_etherpad-lite/static/js/pluginfw/plugins");\
|
||||||
|
parent_req.getRequirementFromParent(require, "./pluginfw/hooks");\
|
||||||
|
parent_req.getRequirementFromParent(require, "./pluginfw/plugins");\
|
||||||
require.define("/plugins", null);\n\
|
require.define("/plugins", null);\n\
|
||||||
require.define("/plugins.js", function (require, exports, module) {\
|
require.define("/plugins.js", function (require, exports, module) {\
|
||||||
module.exports = parent.parent.require("/plugins");\
|
module.exports = require("ep_etherpad-lite/static/js/plugins");\
|
||||||
});\
|
});\
|
||||||
</script>\
|
</script>\
|
||||||
');
|
');
|
||||||
|
@ -271,7 +288,7 @@ function Ace2Editor()
|
||||||
var thisFunctionsName = "ChildAccessibleAce2Editor";
|
var thisFunctionsName = "ChildAccessibleAce2Editor";
|
||||||
(function () {return this}())[thisFunctionsName] = Ace2Editor;
|
(function () {return this}())[thisFunctionsName] = Ace2Editor;
|
||||||
|
|
||||||
var outerScript = 'editorId = "' + info.id + '"; editorInfo = parent.' + thisFunctionsName + '.registry[editorId]; ' + 'window.onload = function() ' + '{ window.onload = null; setTimeout' + '(function() ' + '{ var iframe = document.createElement("IFRAME"); ' + 'iframe.scrolling = "no"; var outerdocbody = document.getElementById("outerdocbody"); ' + 'iframe.frameBorder = 0; iframe.allowTransparency = true; ' + // for IE
|
var outerScript = 'editorId = "' + info.id + '"; editorInfo = parent.' + thisFunctionsName + '.registry[editorId]; ' + 'window.onload = function() ' + '{ window.onload = null; setTimeout' + '(function() ' + '{ var iframe = document.createElement("IFRAME"); iframe.name = "ace_inner";' + 'iframe.scrolling = "no"; var outerdocbody = document.getElementById("outerdocbody"); ' + 'iframe.frameBorder = 0; iframe.allowTransparency = true; ' + // for IE
|
||||||
'outerdocbody.insertBefore(iframe, outerdocbody.firstChild); ' + 'iframe.ace_outerWin = window; ' + 'readyFunc = function() { editorInfo.onEditorReady(); readyFunc = null; editorInfo = null; }; ' + 'var doc = iframe.contentWindow.document; doc.open(); var text = (' + JSON.stringify(iframeHTML.join('\n')) + ');doc.write(text); doc.close(); ' + '}, 0); }';
|
'outerdocbody.insertBefore(iframe, outerdocbody.firstChild); ' + 'iframe.ace_outerWin = window; ' + 'readyFunc = function() { editorInfo.onEditorReady(); readyFunc = null; editorInfo = null; }; ' + 'var doc = iframe.contentWindow.document; doc.open(); var text = (' + JSON.stringify(iframeHTML.join('\n')) + ');doc.write(text); doc.close(); ' + '}, 0); }';
|
||||||
|
|
||||||
var outerHTML = [doctype, '<html><head>']
|
var outerHTML = [doctype, '<html><head>']
|
||||||
|
@ -281,6 +298,11 @@ function Ace2Editor()
|
||||||
$$INCLUDE_CSS("../static/css/iframe_editor.css");
|
$$INCLUDE_CSS("../static/css/iframe_editor.css");
|
||||||
$$INCLUDE_CSS("../static/css/pad.css");
|
$$INCLUDE_CSS("../static/css/pad.css");
|
||||||
$$INCLUDE_CSS("../static/custom/pad.css");
|
$$INCLUDE_CSS("../static/custom/pad.css");
|
||||||
|
|
||||||
|
|
||||||
|
var additionalCSS = _(hooks.callAll("aceEditorCSS")).map(function(path){ return '../static/plugins/' + path });
|
||||||
|
includedCSS = includedCSS.concat(additionalCSS);
|
||||||
|
|
||||||
pushStyleTagsFor(outerHTML, includedCSS);
|
pushStyleTagsFor(outerHTML, includedCSS);
|
||||||
|
|
||||||
// bizarrely, in FF2, a file with no "external" dependencies won't finish loading properly
|
// bizarrely, in FF2, a file with no "external" dependencies won't finish loading properly
|
||||||
|
@ -288,6 +310,7 @@ function Ace2Editor()
|
||||||
outerHTML.push('<link rel="stylesheet" type="text/css" href="data:text/css,"/>', '\x3cscript>\n', outerScript.replace(/<\//g, '<\\/'), '\n\x3c/script>', '</head><body id="outerdocbody"><div id="sidediv"><!-- --></div><div id="linemetricsdiv">x</div><div id="overlaysdiv"><!-- --></div></body></html>');
|
outerHTML.push('<link rel="stylesheet" type="text/css" href="data:text/css,"/>', '\x3cscript>\n', outerScript.replace(/<\//g, '<\\/'), '\n\x3c/script>', '</head><body id="outerdocbody"><div id="sidediv"><!-- --></div><div id="linemetricsdiv">x</div><div id="overlaysdiv"><!-- --></div></body></html>');
|
||||||
|
|
||||||
var outerFrame = document.createElement("IFRAME");
|
var outerFrame = document.createElement("IFRAME");
|
||||||
|
outerFrame.name = "ace_outer";
|
||||||
outerFrame.frameBorder = 0; // for IE
|
outerFrame.frameBorder = 0; // for IE
|
||||||
info.frame = outerFrame;
|
info.frame = outerFrame;
|
||||||
document.getElementById(containerId).appendChild(outerFrame);
|
document.getElementById(containerId).appendChild(outerFrame);
|
|
@ -20,7 +20,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var Security = require('/security');
|
var Security = require('./security');
|
||||||
|
|
||||||
function isNodeText(node)
|
function isNodeText(node)
|
||||||
{
|
{
|
||||||
|
@ -29,58 +29,10 @@ function isNodeText(node)
|
||||||
|
|
||||||
function object(o)
|
function object(o)
|
||||||
{
|
{
|
||||||
var f = function()
|
var f = function(){};
|
||||||
{};
|
|
||||||
f.prototype = o;
|
f.prototype = o;
|
||||||
return new f();
|
return new f();
|
||||||
}
|
}
|
||||||
|
|
||||||
function extend(obj, props)
|
|
||||||
{
|
|
||||||
for (var p in props)
|
|
||||||
{
|
|
||||||
obj[p] = props[p];
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
function forEach(array, func)
|
|
||||||
{
|
|
||||||
for (var i = 0; i < array.length; i++)
|
|
||||||
{
|
|
||||||
var result = func(array[i], i);
|
|
||||||
if (result) break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function map(array, func)
|
|
||||||
{
|
|
||||||
var result = [];
|
|
||||||
// must remain compatible with "arguments" pseudo-array
|
|
||||||
for (var i = 0; i < array.length; i++)
|
|
||||||
{
|
|
||||||
if (func) result.push(func(array[i], i));
|
|
||||||
else result.push(array[i]);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function filter(array, func)
|
|
||||||
{
|
|
||||||
var result = [];
|
|
||||||
// must remain compatible with "arguments" pseudo-array
|
|
||||||
for (var i = 0; i < array.length; i++)
|
|
||||||
{
|
|
||||||
if (func(array[i], i)) result.push(array[i]);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isArray(testObject)
|
|
||||||
{
|
|
||||||
return testObject && typeof testObject === 'object' && !(testObject.propertyIsEnumerable('length')) && typeof testObject.length === 'number';
|
|
||||||
}
|
|
||||||
|
|
||||||
var userAgent = (((function () {return this;})().navigator || {}).userAgent || 'node-js').toLowerCase();
|
var userAgent = (((function () {return this;})().navigator || {}).userAgent || 'node-js').toLowerCase();
|
||||||
|
|
||||||
// Figure out what browser is being used (stolen from jquery 1.2.1)
|
// Figure out what browser is being used (stolen from jquery 1.2.1)
|
||||||
|
@ -142,21 +94,13 @@ function htmlPrettyEscape(str)
|
||||||
}
|
}
|
||||||
|
|
||||||
var noop = function(){};
|
var noop = function(){};
|
||||||
var identity = function(x){return x};
|
|
||||||
|
|
||||||
exports.isNodeText = isNodeText;
|
exports.isNodeText = isNodeText;
|
||||||
exports.object = object;
|
exports.object = object;
|
||||||
exports.extend = extend;
|
|
||||||
exports.forEach = forEach;
|
|
||||||
exports.map = map;
|
|
||||||
exports.filter = filter;
|
|
||||||
exports.isArray = isArray;
|
|
||||||
exports.browser = browser;
|
exports.browser = browser;
|
||||||
exports.getAssoc = getAssoc;
|
exports.getAssoc = getAssoc;
|
||||||
exports.setAssoc = setAssoc;
|
exports.setAssoc = setAssoc;
|
||||||
exports.binarySearch = binarySearch;
|
exports.binarySearch = binarySearch;
|
||||||
exports.binarySearchInfinite = binarySearchInfinite;
|
exports.binarySearchInfinite = binarySearchInfinite;
|
||||||
exports.htmlPrettyEscape = htmlPrettyEscape;
|
exports.htmlPrettyEscape = htmlPrettyEscape;
|
||||||
exports.map = map;
|
|
||||||
exports.noop = noop;
|
exports.noop = noop;
|
||||||
exports.identity = identity;
|
|
|
@ -19,39 +19,41 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
var editor, _, $, jQuery, plugins, Ace2Common;
|
||||||
|
|
||||||
var Ace2Common = require('/ace2_common');
|
Ace2Common = require('./ace2_common');
|
||||||
|
|
||||||
// Extract useful method defined in the other module.
|
plugins = require('ep_etherpad-lite/static/js/pluginfw/plugins');
|
||||||
var isNodeText = Ace2Common.isNodeText;
|
$ = jQuery = require('./rjquery').$;
|
||||||
var object = Ace2Common.object;
|
_ = require("./underscore");
|
||||||
var extend = Ace2Common.extend;
|
|
||||||
var forEach = Ace2Common.forEach;
|
|
||||||
var map = Ace2Common.map;
|
|
||||||
var filter = Ace2Common.filter;
|
|
||||||
var isArray = Ace2Common.isArray;
|
|
||||||
var browser = Ace2Common.browser;
|
|
||||||
var getAssoc = Ace2Common.getAssoc;
|
|
||||||
var setAssoc = Ace2Common.setAssoc;
|
|
||||||
var binarySearchInfinite = Ace2Common.binarySearchInfinite;
|
|
||||||
var htmlPrettyEscape = Ace2Common.htmlPrettyEscape;
|
|
||||||
var map = Ace2Common.map;
|
|
||||||
var noop = Ace2Common.noop;
|
|
||||||
|
|
||||||
var makeChangesetTracker = require('/changesettracker').makeChangesetTracker;
|
|
||||||
var colorutils = require('/colorutils').colorutils;
|
|
||||||
var makeContentCollector = require('/contentcollector').makeContentCollector;
|
|
||||||
var makeCSSManager = require('/cssmanager').makeCSSManager;
|
|
||||||
var domline = require('/domline').domline;
|
|
||||||
var AttribPool = require('/AttributePoolFactory').createAttributePool;
|
|
||||||
var Changeset = require('/Changeset');
|
|
||||||
var linestylefilter = require('/linestylefilter').linestylefilter;
|
|
||||||
var newSkipList = require('/skiplist').newSkipList;
|
|
||||||
var undoModule = require('/undomodule').undoModule;
|
|
||||||
var makeVirtualLineView = require('/virtual_lines').makeVirtualLineView;
|
|
||||||
|
|
||||||
|
var isNodeText = Ace2Common.isNodeText,
|
||||||
|
browser = Ace2Common.browser,
|
||||||
|
getAssoc = Ace2Common.getAssoc,
|
||||||
|
setAssoc = Ace2Common.setAssoc,
|
||||||
|
isTextNode = Ace2Common.isTextNode,
|
||||||
|
binarySearchInfinite = Ace2Common.binarySearchInfinite,
|
||||||
|
htmlPrettyEscape = Ace2Common.htmlPrettyEscape,
|
||||||
|
noop = Ace2Common.noop;
|
||||||
|
var hooks = require('./pluginfw/hooks');
|
||||||
|
|
||||||
|
|
||||||
function Ace2Inner(){
|
function Ace2Inner(){
|
||||||
|
|
||||||
|
var makeChangesetTracker = require('./changesettracker').makeChangesetTracker;
|
||||||
|
var colorutils = require('./colorutils').colorutils;
|
||||||
|
var makeContentCollector = require('./contentcollector').makeContentCollector;
|
||||||
|
var makeCSSManager = require('./cssmanager').makeCSSManager;
|
||||||
|
var domline = require('./domline').domline;
|
||||||
|
var AttribPool = require('./AttributePool');
|
||||||
|
var Changeset = require('./Changeset');
|
||||||
|
var ChangesetUtils = require('./ChangesetUtils');
|
||||||
|
var linestylefilter = require('./linestylefilter').linestylefilter;
|
||||||
|
var SkipList = require('./skiplist');
|
||||||
|
var undoModule = require('./undomodule').undoModule;
|
||||||
|
var makeVirtualLineView = require('./virtual_lines').makeVirtualLineView;
|
||||||
|
var AttributeManager = require('./AttributeManager');
|
||||||
|
|
||||||
var DEBUG = false; //$$ build script replaces the string "var DEBUG=true;//$$" with "var DEBUG=false;"
|
var DEBUG = false; //$$ build script replaces the string "var DEBUG=true;//$$" with "var DEBUG=false;"
|
||||||
// changed to false
|
// changed to false
|
||||||
var isSetUp = false;
|
var isSetUp = false;
|
||||||
|
@ -70,7 +72,6 @@ function Ace2Inner(){
|
||||||
var thisAuthor = '';
|
var thisAuthor = '';
|
||||||
|
|
||||||
var disposed = false;
|
var disposed = false;
|
||||||
|
|
||||||
var editorInfo = parent.editorInfo;
|
var editorInfo = parent.editorInfo;
|
||||||
|
|
||||||
var iframe = window.frameElement;
|
var iframe = window.frameElement;
|
||||||
|
@ -81,21 +82,18 @@ function Ace2Inner(){
|
||||||
var overlaysdiv = lineMetricsDiv.nextSibling;
|
var overlaysdiv = lineMetricsDiv.nextSibling;
|
||||||
initLineNumbers();
|
initLineNumbers();
|
||||||
|
|
||||||
var outsideKeyDown = function(evt)
|
var outsideKeyDown = noop;
|
||||||
{};
|
|
||||||
var outsideKeyPress = function(evt)
|
var outsideKeyPress = function(){return true;};
|
||||||
{
|
|
||||||
return true;
|
var outsideNotifyDirty = noop;
|
||||||
};
|
|
||||||
var outsideNotifyDirty = function()
|
|
||||||
{};
|
|
||||||
|
|
||||||
// selFocusAtStart -- determines whether the selection extends "backwards", so that the focus
|
// selFocusAtStart -- determines whether the selection extends "backwards", so that the focus
|
||||||
// point (controlled with the arrow keys) is at the beginning; not supported in IE, though
|
// point (controlled with the arrow keys) is at the beginning; not supported in IE, though
|
||||||
// native IE selections have that behavior (which we try not to interfere with).
|
// native IE selections have that behavior (which we try not to interfere with).
|
||||||
// Must be false if selection is collapsed!
|
// Must be false if selection is collapsed!
|
||||||
var rep = {
|
var rep = {
|
||||||
lines: newSkipList(),
|
lines: new SkipList(),
|
||||||
selStart: null,
|
selStart: null,
|
||||||
selEnd: null,
|
selEnd: null,
|
||||||
selFocusAtStart: false,
|
selFocusAtStart: false,
|
||||||
|
@ -103,6 +101,7 @@ function Ace2Inner(){
|
||||||
alines: [],
|
alines: [],
|
||||||
apool: new AttribPool()
|
apool: new AttribPool()
|
||||||
};
|
};
|
||||||
|
|
||||||
// lines, alltext, alines, and DOM are set up in setup()
|
// lines, alltext, alines, and DOM are set up in setup()
|
||||||
if (undoModule.enabled)
|
if (undoModule.enabled)
|
||||||
{
|
{
|
||||||
|
@ -122,6 +121,7 @@ function Ace2Inner(){
|
||||||
iframePadRight = 0;
|
iframePadRight = 0;
|
||||||
|
|
||||||
var console = (DEBUG && window.console);
|
var console = (DEBUG && window.console);
|
||||||
|
var documentAttributeManager;
|
||||||
|
|
||||||
if (!window.console)
|
if (!window.console)
|
||||||
{
|
{
|
||||||
|
@ -158,6 +158,7 @@ function Ace2Inner(){
|
||||||
|
|
||||||
var textFace = 'monospace';
|
var textFace = 'monospace';
|
||||||
var textSize = 12;
|
var textSize = 12;
|
||||||
|
|
||||||
|
|
||||||
function textLineHeight()
|
function textLineHeight()
|
||||||
{
|
{
|
||||||
|
@ -685,7 +686,7 @@ function Ace2Inner(){
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
lines = map(text.split('\n'), textify);
|
lines = _.map(text.split('\n'), textify);
|
||||||
}
|
}
|
||||||
var newText = "\n";
|
var newText = "\n";
|
||||||
if (lines.length > 0)
|
if (lines.length > 0)
|
||||||
|
@ -847,7 +848,7 @@ function Ace2Inner(){
|
||||||
var cmdArgs = Array.prototype.slice.call(arguments, 1);
|
var cmdArgs = Array.prototype.slice.call(arguments, 1);
|
||||||
if (CMDS[cmd])
|
if (CMDS[cmd])
|
||||||
{
|
{
|
||||||
inCallStack(cmd, function()
|
inCallStackIfNecessary(cmd, function()
|
||||||
{
|
{
|
||||||
fastIncorp(9);
|
fastIncorp(9);
|
||||||
CMDS[cmd].apply(CMDS, cmdArgs);
|
CMDS[cmd].apply(CMDS, cmdArgs);
|
||||||
|
@ -857,7 +858,7 @@ function Ace2Inner(){
|
||||||
|
|
||||||
function replaceRange(start, end, text)
|
function replaceRange(start, end, text)
|
||||||
{
|
{
|
||||||
inCallStack('replaceRange', function()
|
inCallStackIfNecessary('replaceRange', function()
|
||||||
{
|
{
|
||||||
fastIncorp(9);
|
fastIncorp(9);
|
||||||
performDocumentReplaceRange(start, end, text);
|
performDocumentReplaceRange(start, end, text);
|
||||||
|
@ -884,9 +885,7 @@ function Ace2Inner(){
|
||||||
{
|
{
|
||||||
return fn(editorInfo);
|
return fn(editorInfo);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (normalize !== undefined)
|
if (normalize !== undefined)
|
||||||
{
|
{
|
||||||
var wrapper1 = wrapper;
|
var wrapper1 = wrapper;
|
||||||
|
@ -934,7 +933,10 @@ function Ace2Inner(){
|
||||||
},
|
},
|
||||||
grayedout: setClassPresenceNamed(outerWin.document.body, "grayedout"),
|
grayedout: setClassPresenceNamed(outerWin.document.body, "grayedout"),
|
||||||
dmesg: function(){ dmesg = window.dmesg = value; },
|
dmesg: function(){ dmesg = window.dmesg = value; },
|
||||||
userauthor: function(value){ thisAuthor = String(value); },
|
userauthor: function(value){
|
||||||
|
thisAuthor = String(value);
|
||||||
|
documentAttributeManager.author = thisAuthor;
|
||||||
|
},
|
||||||
styled: setStyled,
|
styled: setStyled,
|
||||||
textface: setTextFace,
|
textface: setTextFace,
|
||||||
textsize: setTextSize,
|
textsize: setTextSize,
|
||||||
|
@ -1160,7 +1162,7 @@ function Ace2Inner(){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
inCallStack("idleWorkTimer", function()
|
inCallStackIfNecessary("idleWorkTimer", function()
|
||||||
{
|
{
|
||||||
|
|
||||||
var isTimeUp = newTimeLimit(250);
|
var isTimeUp = newTimeLimit(250);
|
||||||
|
@ -1623,8 +1625,7 @@ function Ace2Inner(){
|
||||||
}
|
}
|
||||||
//var fragment = magicdom.wrapDom(document.createDocumentFragment());
|
//var fragment = magicdom.wrapDom(document.createDocumentFragment());
|
||||||
domInsertsNeeded.push([nodeToAddAfter, lineNodeInfos]);
|
domInsertsNeeded.push([nodeToAddAfter, lineNodeInfos]);
|
||||||
forEach(dirtyNodes, function(n)
|
_.each(dirtyNodes,function(n){
|
||||||
{
|
|
||||||
toDeleteAtEnd.push(n);
|
toDeleteAtEnd.push(n);
|
||||||
});
|
});
|
||||||
var spliceHints = {};
|
var spliceHints = {};
|
||||||
|
@ -1646,7 +1647,7 @@ function Ace2Inner(){
|
||||||
|
|
||||||
// update the representation
|
// update the representation
|
||||||
p.mark("splice");
|
p.mark("splice");
|
||||||
forEach(splicesToDo, function(splice)
|
_.each(splicesToDo, function(splice)
|
||||||
{
|
{
|
||||||
doIncorpLineSplice(splice[0], splice[1], splice[2], splice[3], splice[4]);
|
doIncorpLineSplice(splice[0], splice[1], splice[2], splice[3], splice[4]);
|
||||||
});
|
});
|
||||||
|
@ -1656,14 +1657,14 @@ function Ace2Inner(){
|
||||||
//var isTimeUp = newTimeLimit(100);
|
//var isTimeUp = newTimeLimit(100);
|
||||||
// do DOM inserts
|
// do DOM inserts
|
||||||
p.mark("insert");
|
p.mark("insert");
|
||||||
forEach(domInsertsNeeded, function(ins)
|
_.each(domInsertsNeeded,function(ins)
|
||||||
{
|
{
|
||||||
insertDomLines(ins[0], ins[1], isTimeUp);
|
insertDomLines(ins[0], ins[1], isTimeUp);
|
||||||
});
|
});
|
||||||
|
|
||||||
p.mark("del");
|
p.mark("del");
|
||||||
// delete old dom nodes
|
// delete old dom nodes
|
||||||
forEach(toDeleteAtEnd, function(n)
|
_.each(toDeleteAtEnd,function(n)
|
||||||
{
|
{
|
||||||
//var id = n.uniqueId();
|
//var id = n.uniqueId();
|
||||||
// parent of n may not be "root" in IE due to non-tree-shaped DOM (wtf)
|
// parent of n may not be "root" in IE due to non-tree-shaped DOM (wtf)
|
||||||
|
@ -1773,7 +1774,7 @@ function Ace2Inner(){
|
||||||
var charEnd = rep.lines.offsetOfEntry(endEntry) + endEntry.width;
|
var charEnd = rep.lines.offsetOfEntry(endEntry) + endEntry.width;
|
||||||
|
|
||||||
//rep.lexer.lexCharRange([charStart, charEnd], isTimeUp);
|
//rep.lexer.lexCharRange([charStart, charEnd], isTimeUp);
|
||||||
forEach(infoStructs, function(info)
|
_.each(infoStructs, function(info)
|
||||||
{
|
{
|
||||||
var p2 = PROFILER("insertLine", false);
|
var p2 = PROFILER("insertLine", false);
|
||||||
var node = info.node;
|
var node = info.node;
|
||||||
|
@ -1870,55 +1871,6 @@ function Ace2Inner(){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function setupMozillaCaretHack(lineNum)
|
|
||||||
{
|
|
||||||
// This is really ugly, but by god, it works!
|
|
||||||
// Fixes annoying Firefox caret artifact (observed in 2.0.0.12
|
|
||||||
// and unfixed in Firefox 2 as of now) where mutating the DOM
|
|
||||||
// and then moving the caret to the beginning of a line causes
|
|
||||||
// an image of the caret to be XORed at the top of the iframe.
|
|
||||||
// The previous solution involved remembering to set the selection
|
|
||||||
// later, in response to the next event in the queue, which was hugely
|
|
||||||
// annoying.
|
|
||||||
// This solution: add a space character (0x20) to the beginning of the line.
|
|
||||||
// After setting the selection, remove the space.
|
|
||||||
var lineNode = rep.lines.atIndex(lineNum).lineNode;
|
|
||||||
|
|
||||||
var fc = lineNode.firstChild;
|
|
||||||
while (isBlockElement(fc) && fc.firstChild)
|
|
||||||
{
|
|
||||||
fc = fc.firstChild;
|
|
||||||
}
|
|
||||||
var textNode;
|
|
||||||
if (isNodeText(fc))
|
|
||||||
{
|
|
||||||
fc.nodeValue = " " + fc.nodeValue;
|
|
||||||
textNode = fc;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
textNode = doc.createTextNode(" ");
|
|
||||||
fc.parentNode.insertBefore(textNode, fc);
|
|
||||||
}
|
|
||||||
markNodeClean(lineNode);
|
|
||||||
return {
|
|
||||||
unhack: function()
|
|
||||||
{
|
|
||||||
if (textNode.nodeValue == " ")
|
|
||||||
{
|
|
||||||
textNode.parentNode.removeChild(textNode);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
textNode.nodeValue = textNode.nodeValue.substring(1);
|
|
||||||
}
|
|
||||||
markNodeClean(lineNode);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function getPointForLineAndChar(lineAndChar)
|
function getPointForLineAndChar(lineAndChar)
|
||||||
{
|
{
|
||||||
var line = lineAndChar[0];
|
var line = lineAndChar[0];
|
||||||
|
@ -2049,6 +2001,7 @@ function Ace2Inner(){
|
||||||
return [lineNum, col];
|
return [lineNum, col];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
editorInfo.ace_getLineAndCharForPoint = getLineAndCharForPoint;
|
||||||
|
|
||||||
function createDomLineEntry(lineString)
|
function createDomLineEntry(lineString)
|
||||||
{
|
{
|
||||||
|
@ -2084,10 +2037,8 @@ function Ace2Inner(){
|
||||||
var linesMutatee = {
|
var linesMutatee = {
|
||||||
splice: function(start, numRemoved, newLinesVA)
|
splice: function(start, numRemoved, newLinesVA)
|
||||||
{
|
{
|
||||||
domAndRepSplice(start, numRemoved, map(Array.prototype.slice.call(arguments, 2), function(s)
|
var args = Array.prototype.slice.call(arguments, 2);
|
||||||
{
|
domAndRepSplice(start, numRemoved, _.map(args, function(s){ return s.slice(0, -1); }), null);
|
||||||
return s.slice(0, -1);
|
|
||||||
}), null);
|
|
||||||
},
|
},
|
||||||
get: function(i)
|
get: function(i)
|
||||||
{
|
{
|
||||||
|
@ -2099,7 +2050,7 @@ function Ace2Inner(){
|
||||||
},
|
},
|
||||||
slice_notused: function(start, end)
|
slice_notused: function(start, end)
|
||||||
{
|
{
|
||||||
return map(rep.lines.slice(start, end), function(e)
|
return _.map(rep.lines.slice(start, end), function(e)
|
||||||
{
|
{
|
||||||
return e.text + '\n';
|
return e.text + '\n';
|
||||||
});
|
});
|
||||||
|
@ -2132,7 +2083,7 @@ function Ace2Inner(){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var lineEntries = map(newLineStrings, createDomLineEntry);
|
var lineEntries = _.map(newLineStrings, createDomLineEntry);
|
||||||
|
|
||||||
doRepLineSplice(startLine, deleteCount, lineEntries);
|
doRepLineSplice(startLine, deleteCount, lineEntries);
|
||||||
|
|
||||||
|
@ -2143,12 +2094,12 @@ function Ace2Inner(){
|
||||||
}
|
}
|
||||||
else nodeToAddAfter = null;
|
else nodeToAddAfter = null;
|
||||||
|
|
||||||
insertDomLines(nodeToAddAfter, map(lineEntries, function(entry)
|
insertDomLines(nodeToAddAfter, _.map(lineEntries, function(entry)
|
||||||
{
|
{
|
||||||
return entry.domInfo;
|
return entry.domInfo;
|
||||||
}), isTimeUp);
|
}), isTimeUp);
|
||||||
|
|
||||||
forEach(keysToDelete, function(k)
|
_.each(keysToDelete, function(k)
|
||||||
{
|
{
|
||||||
var n = doc.getElementById(k);
|
var n = doc.getElementById(k);
|
||||||
n.parentNode.removeChild(n);
|
n.parentNode.removeChild(n);
|
||||||
|
@ -2254,6 +2205,9 @@ function Ace2Inner(){
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Converts the position of a char (index in String) into a [row, col] tuple
|
||||||
|
*/
|
||||||
function lineAndColumnFromChar(x)
|
function lineAndColumnFromChar(x)
|
||||||
{
|
{
|
||||||
var lineEntry = rep.lines.atOffset(x);
|
var lineEntry = rep.lines.atOffset(x);
|
||||||
|
@ -2308,8 +2262,8 @@ function Ace2Inner(){
|
||||||
// CCCC\n
|
// CCCC\n
|
||||||
// end[0]: <CCC end[1] CCC>-------\n
|
// end[0]: <CCC end[1] CCC>-------\n
|
||||||
var builder = Changeset.builder(rep.lines.totalWidth());
|
var builder = Changeset.builder(rep.lines.totalWidth());
|
||||||
buildKeepToStartOfRange(builder, start);
|
ChangesetUtils.buildKeepToStartOfRange(rep, builder, start);
|
||||||
buildRemoveRange(builder, start, end);
|
ChangesetUtils.buildRemoveRange(rep, builder, start, end);
|
||||||
builder.insert(newText, [
|
builder.insert(newText, [
|
||||||
['author', thisAuthor]
|
['author', thisAuthor]
|
||||||
], rep.apool);
|
], rep.apool);
|
||||||
|
@ -2320,68 +2274,17 @@ function Ace2Inner(){
|
||||||
|
|
||||||
function performDocumentApplyAttributesToCharRange(start, end, attribs)
|
function performDocumentApplyAttributesToCharRange(start, end, attribs)
|
||||||
{
|
{
|
||||||
if (end >= rep.alltext.length)
|
end = Math.min(end, rep.alltext.length - 1);
|
||||||
{
|
documentAttributeManager.setAttributesOnRange(lineAndColumnFromChar(start), lineAndColumnFromChar(end), attribs);
|
||||||
end = rep.alltext.length - 1;
|
|
||||||
}
|
|
||||||
performDocumentApplyAttributesToRange(lineAndColumnFromChar(start), lineAndColumnFromChar(end), attribs);
|
|
||||||
}
|
}
|
||||||
editorInfo.ace_performDocumentApplyAttributesToCharRange = performDocumentApplyAttributesToCharRange;
|
editorInfo.ace_performDocumentApplyAttributesToCharRange = performDocumentApplyAttributesToCharRange;
|
||||||
|
|
||||||
function performDocumentApplyAttributesToRange(start, end, attribs)
|
|
||||||
{
|
|
||||||
var builder = Changeset.builder(rep.lines.totalWidth());
|
|
||||||
buildKeepToStartOfRange(builder, start);
|
|
||||||
buildKeepRange(builder, start, end, attribs, rep.apool);
|
|
||||||
var cs = builder.toString();
|
|
||||||
performDocumentApplyChangeset(cs);
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildKeepToStartOfRange(builder, start)
|
|
||||||
{
|
|
||||||
var startLineOffset = rep.lines.offsetOfIndex(start[0]);
|
|
||||||
|
|
||||||
builder.keep(startLineOffset, start[0]);
|
|
||||||
builder.keep(start[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildRemoveRange(builder, start, end)
|
|
||||||
{
|
|
||||||
var startLineOffset = rep.lines.offsetOfIndex(start[0]);
|
|
||||||
var endLineOffset = rep.lines.offsetOfIndex(end[0]);
|
|
||||||
|
|
||||||
if (end[0] > start[0])
|
|
||||||
{
|
|
||||||
builder.remove(endLineOffset - startLineOffset - start[1], end[0] - start[0]);
|
|
||||||
builder.remove(end[1]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
builder.remove(end[1] - start[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildKeepRange(builder, start, end, attribs, pool)
|
|
||||||
{
|
|
||||||
var startLineOffset = rep.lines.offsetOfIndex(start[0]);
|
|
||||||
var endLineOffset = rep.lines.offsetOfIndex(end[0]);
|
|
||||||
|
|
||||||
if (end[0] > start[0])
|
|
||||||
{
|
|
||||||
builder.keep(endLineOffset - startLineOffset - start[1], end[0] - start[0], attribs, pool);
|
|
||||||
builder.keep(end[1], 0, attribs, pool);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
builder.keep(end[1] - start[1], 0, attribs, pool);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setAttributeOnSelection(attributeName, attributeValue)
|
function setAttributeOnSelection(attributeName, attributeValue)
|
||||||
{
|
{
|
||||||
if (!(rep.selStart && rep.selEnd)) return;
|
if (!(rep.selStart && rep.selEnd)) return;
|
||||||
|
|
||||||
performDocumentApplyAttributesToRange(rep.selStart, rep.selEnd, [
|
documentAttributeManager.setAttributesOnRange(rep.selStart, rep.selEnd, [
|
||||||
[attributeName, attributeValue]
|
[attributeName, attributeValue]
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
@ -2442,13 +2345,13 @@ function Ace2Inner(){
|
||||||
|
|
||||||
if (selectionAllHasIt)
|
if (selectionAllHasIt)
|
||||||
{
|
{
|
||||||
performDocumentApplyAttributesToRange(rep.selStart, rep.selEnd, [
|
documentAttributeManager.setAttributesOnRange(rep.selStart, rep.selEnd, [
|
||||||
[attributeName, '']
|
[attributeName, '']
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
performDocumentApplyAttributesToRange(rep.selStart, rep.selEnd, [
|
documentAttributeManager.setAttributesOnRange(rep.selStart, rep.selEnd, [
|
||||||
[attributeName, 'true']
|
[attributeName, 'true']
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
@ -2468,7 +2371,7 @@ function Ace2Inner(){
|
||||||
function doRepLineSplice(startLine, deleteCount, newLineEntries)
|
function doRepLineSplice(startLine, deleteCount, newLineEntries)
|
||||||
{
|
{
|
||||||
|
|
||||||
forEach(newLineEntries, function(entry)
|
_.each(newLineEntries, function(entry)
|
||||||
{
|
{
|
||||||
entry.width = entry.text.length + 1;
|
entry.width = entry.text.length + 1;
|
||||||
});
|
});
|
||||||
|
@ -2483,7 +2386,7 @@ function Ace2Inner(){
|
||||||
currentCallStack.repChanged = true;
|
currentCallStack.repChanged = true;
|
||||||
var newRegionEnd = rep.lines.offsetOfIndex(startLine + newLineEntries.length);
|
var newRegionEnd = rep.lines.offsetOfIndex(startLine + newLineEntries.length);
|
||||||
|
|
||||||
var newText = map(newLineEntries, function(e)
|
var newText = _.map(newLineEntries, function(e)
|
||||||
{
|
{
|
||||||
return e.text + '\n';
|
return e.text + '\n';
|
||||||
}).join('');
|
}).join('');
|
||||||
|
@ -2513,7 +2416,7 @@ function Ace2Inner(){
|
||||||
selEndHintChar = rep.lines.offsetOfIndex(hints.selEnd[0]) + hints.selEnd[1] - oldRegionStart;
|
selEndHintChar = rep.lines.offsetOfIndex(hints.selEnd[0]) + hints.selEnd[1] - oldRegionStart;
|
||||||
}
|
}
|
||||||
|
|
||||||
var newText = map(newLineEntries, function(e)
|
var newText = _.map(newLineEntries, function(e)
|
||||||
{
|
{
|
||||||
return e.text + '\n';
|
return e.text + '\n';
|
||||||
}).join('');
|
}).join('');
|
||||||
|
@ -2861,6 +2764,7 @@ function Ace2Inner(){
|
||||||
currentCallStack.selectionAffected = true;
|
currentCallStack.selectionAffected = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
editorInfo.ace_performSelectionChange = performSelectionChange;
|
||||||
|
|
||||||
// Change the abstract representation of the document to have a different selection.
|
// Change the abstract representation of the document to have a different selection.
|
||||||
// Should not rely on the line representation. Should not affect the DOM.
|
// Should not rely on the line representation. Should not affect the DOM.
|
||||||
|
@ -2969,6 +2873,10 @@ function Ace2Inner(){
|
||||||
"ul": 1
|
"ul": 1
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_.each(hooks.callAll('aceRegisterBlockElements'), function(element){
|
||||||
|
_blockElems[element] = 1;
|
||||||
|
});
|
||||||
|
|
||||||
function isBlockElement(n)
|
function isBlockElement(n)
|
||||||
{
|
{
|
||||||
return !!_blockElems[(n.tagName || "").toLowerCase()];
|
return !!_blockElems[(n.tagName || "").toLowerCase()];
|
||||||
|
@ -3054,7 +2962,7 @@ function Ace2Inner(){
|
||||||
{
|
{
|
||||||
// returns index of cleanRange containing i, or -1 if none
|
// returns index of cleanRange containing i, or -1 if none
|
||||||
var answer = -1;
|
var answer = -1;
|
||||||
forEach(cleanRanges, function(r, idx)
|
_.each(cleanRanges ,function(r, idx)
|
||||||
{
|
{
|
||||||
if (i >= r[1]) return false; // keep looking
|
if (i >= r[1]) return false; // keep looking
|
||||||
if (i < r[0]) return true; // not found, stop looking
|
if (i < r[0]) return true; // not found, stop looking
|
||||||
|
@ -3288,7 +3196,7 @@ function Ace2Inner(){
|
||||||
|
|
||||||
function handleClick(evt)
|
function handleClick(evt)
|
||||||
{
|
{
|
||||||
inCallStack("handleClick", function()
|
inCallStackIfNecessary("handleClick", function()
|
||||||
{
|
{
|
||||||
idleWorkTimer.atMost(200);
|
idleWorkTimer.atMost(200);
|
||||||
});
|
});
|
||||||
|
@ -3333,6 +3241,7 @@ function Ace2Inner(){
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var lineNum = rep.selStart[0];
|
var lineNum = rep.selStart[0];
|
||||||
var listType = getLineListType(lineNum);
|
var listType = getLineListType(lineNum);
|
||||||
|
|
||||||
|
@ -3404,11 +3313,9 @@ function Ace2Inner(){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mods.length > 0)
|
_.each(mods, function(mod){
|
||||||
{
|
setLineListType(mod[0], mod[1]);
|
||||||
setLineListTypes(mods);
|
});
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
editorInfo.ace_doIndentOutdent = doIndentOutdent;
|
editorInfo.ace_doIndentOutdent = doIndentOutdent;
|
||||||
|
@ -3458,6 +3365,9 @@ function Ace2Inner(){
|
||||||
var thisLineListType = getLineListType(theLine);
|
var thisLineListType = getLineListType(theLine);
|
||||||
var prevLineEntry = (theLine > 0 && rep.lines.atIndex(theLine - 1));
|
var prevLineEntry = (theLine > 0 && rep.lines.atIndex(theLine - 1));
|
||||||
var prevLineBlank = (prevLineEntry && prevLineEntry.text.length == prevLineEntry.lineMarker);
|
var prevLineBlank = (prevLineEntry && prevLineEntry.text.length == prevLineEntry.lineMarker);
|
||||||
|
|
||||||
|
var thisLineHasMarker = documentAttributeManager.lineHasMarker(theLine);
|
||||||
|
|
||||||
if (thisLineListType)
|
if (thisLineListType)
|
||||||
{
|
{
|
||||||
// this line is a list
|
// this line is a list
|
||||||
|
@ -3471,6 +3381,9 @@ function Ace2Inner(){
|
||||||
// delistify
|
// delistify
|
||||||
performDocumentReplaceRange([theLine, 0], [theLine, lineEntry.lineMarker], '');
|
performDocumentReplaceRange([theLine, 0], [theLine, lineEntry.lineMarker], '');
|
||||||
}
|
}
|
||||||
|
}else if (thisLineHasMarker && prevLineEntry){
|
||||||
|
// If the line has any attributes assigned, remove them by removing the marker '*'
|
||||||
|
performDocumentReplaceRange([theLine -1 , prevLineEntry.text.length], [theLine, lineEntry.lineMarker], '');
|
||||||
}
|
}
|
||||||
else if (theLine > 0)
|
else if (theLine > 0)
|
||||||
{
|
{
|
||||||
|
@ -3610,7 +3523,7 @@ function Ace2Inner(){
|
||||||
|
|
||||||
var stopped = false;
|
var stopped = false;
|
||||||
|
|
||||||
inCallStack("handleKeyEvent", function()
|
inCallStackIfNecessary("handleKeyEvent", function()
|
||||||
{
|
{
|
||||||
|
|
||||||
if (type == "keypress" || (isTypeForSpecialKey && keyCode == 13 /*return*/ ))
|
if (type == "keypress" || (isTypeForSpecialKey && keyCode == 13 /*return*/ ))
|
||||||
|
@ -3823,31 +3736,22 @@ function Ace2Inner(){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var mozillaCaretHack = (false && browser.mozilla && selStart && selEnd && selStart[0] == selEnd[0] && selStart[1] == rep.lines.atIndex(selStart[0]).lineMarker && selEnd[1] == rep.lines.atIndex(selEnd[0]).lineMarker && setupMozillaCaretHack(selStart[0]));
|
|
||||||
|
|
||||||
var selection = {};
|
var selection = {};
|
||||||
|
|
||||||
var ss = [selStart[0], selStart[1]];
|
var ss = [selStart[0], selStart[1]];
|
||||||
if (mozillaCaretHack) ss[1] += 1;
|
|
||||||
selection.startPoint = getPointForLineAndChar(ss);
|
selection.startPoint = getPointForLineAndChar(ss);
|
||||||
|
|
||||||
var se = [selEnd[0], selEnd[1]];
|
var se = [selEnd[0], selEnd[1]];
|
||||||
if (mozillaCaretHack) se[1] += 1;
|
|
||||||
selection.endPoint = getPointForLineAndChar(se);
|
selection.endPoint = getPointForLineAndChar(se);
|
||||||
|
|
||||||
selection.focusAtStart = !! rep.selFocusAtStart;
|
selection.focusAtStart = !! rep.selFocusAtStart;
|
||||||
|
|
||||||
setSelection(selection);
|
setSelection(selection);
|
||||||
|
|
||||||
if (mozillaCaretHack)
|
|
||||||
{
|
|
||||||
mozillaCaretHack.unhack();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRepHTML()
|
function getRepHTML()
|
||||||
{
|
{
|
||||||
return map(rep.lines.slice(), function(entry)
|
return _.map(rep.lines.slice(), function(entry)
|
||||||
{
|
{
|
||||||
var text = entry.text;
|
var text = entry.text;
|
||||||
var content;
|
var content;
|
||||||
|
@ -4533,7 +4437,7 @@ function Ace2Inner(){
|
||||||
|
|
||||||
enforceEditability();
|
enforceEditability();
|
||||||
|
|
||||||
addClass(sideDiv, 'sidedivdelayed');
|
$(sideDiv).addClass('sidedivdelayed');
|
||||||
}
|
}
|
||||||
|
|
||||||
function getScrollXY()
|
function getScrollXY()
|
||||||
|
@ -4586,14 +4490,12 @@ function Ace2Inner(){
|
||||||
|
|
||||||
function teardown()
|
function teardown()
|
||||||
{
|
{
|
||||||
forEach(_teardownActions, function(a)
|
_.each(_teardownActions, function(a)
|
||||||
{
|
{
|
||||||
a();
|
a();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bindEventHandler(window, "load", setup);
|
|
||||||
|
|
||||||
function setDesignMode(newVal)
|
function setDesignMode(newVal)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -4670,20 +4572,20 @@ function Ace2Inner(){
|
||||||
|
|
||||||
function bindTheEventHandlers()
|
function bindTheEventHandlers()
|
||||||
{
|
{
|
||||||
bindEventHandler(document, "keydown", handleKeyEvent);
|
$(document).on("keydown", handleKeyEvent);
|
||||||
bindEventHandler(document, "keypress", handleKeyEvent);
|
$(document).on("keypress", handleKeyEvent);
|
||||||
bindEventHandler(document, "keyup", handleKeyEvent);
|
$(document).on("keyup", handleKeyEvent);
|
||||||
bindEventHandler(document, "click", handleClick);
|
$(document).on("click", handleClick);
|
||||||
bindEventHandler(root, "blur", handleBlur);
|
$(root).on("blur", handleBlur);
|
||||||
if (browser.msie)
|
if (browser.msie)
|
||||||
{
|
{
|
||||||
bindEventHandler(document, "click", handleIEOuterClick);
|
$(document).on("click", handleIEOuterClick);
|
||||||
}
|
}
|
||||||
if (browser.msie) bindEventHandler(root, "paste", handleIEPaste);
|
if (browser.msie) $(root).on("paste", handleIEPaste);
|
||||||
if ((!browser.msie) && document.documentElement)
|
if ((!browser.msie) && document.documentElement)
|
||||||
{
|
{
|
||||||
bindEventHandler(document.documentElement, "compositionstart", handleCompositionEvent);
|
$(document.documentElement).on("compositionstart", handleCompositionEvent);
|
||||||
bindEventHandler(document.documentElement, "compositionend", handleCompositionEvent);
|
$(document.documentElement).on("compositionend", handleCompositionEvent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4699,7 +4601,7 @@ function Ace2Inner(){
|
||||||
}
|
}
|
||||||
|
|
||||||
// click below the body
|
// click below the body
|
||||||
inCallStack("handleOuterClick", function()
|
inCallStackIfNecessary("handleOuterClick", function()
|
||||||
{
|
{
|
||||||
// put caret at bottom of doc
|
// put caret at bottom of doc
|
||||||
fastIncorp(11);
|
fastIncorp(11);
|
||||||
|
@ -4730,49 +4632,16 @@ function Ace2Inner(){
|
||||||
elem.className = array.join(' ');
|
elem.className = array.join(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
function addClass(elem, className)
|
|
||||||
{
|
|
||||||
var seen = false;
|
|
||||||
var cc = getClassArray(elem, function(c)
|
|
||||||
{
|
|
||||||
if (c == className) seen = true;
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
if (!seen)
|
|
||||||
{
|
|
||||||
cc.push(className);
|
|
||||||
setClassArray(elem, cc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeClass(elem, className)
|
|
||||||
{
|
|
||||||
var seen = false;
|
|
||||||
var cc = getClassArray(elem, function(c)
|
|
||||||
{
|
|
||||||
if (c == className)
|
|
||||||
{
|
|
||||||
seen = true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
if (seen)
|
|
||||||
{
|
|
||||||
setClassArray(elem, cc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setClassPresence(elem, className, present)
|
function setClassPresence(elem, className, present)
|
||||||
{
|
{
|
||||||
if (present) addClass(elem, className);
|
if (present) $(elem).addClass(className);
|
||||||
else removeClass(elem, className);
|
else $(elem).removeClass(elem, className);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setup()
|
function setup()
|
||||||
{
|
{
|
||||||
doc = document; // defined as a var in scope outside
|
doc = document; // defined as a var in scope outside
|
||||||
inCallStack("setup", function()
|
inCallStackIfNecessary("setup", function()
|
||||||
{
|
{
|
||||||
var body = doc.getElementById("innerdocbody");
|
var body = doc.getElementById("innerdocbody");
|
||||||
root = body; // defined as a var in scope outside
|
root = body; // defined as a var in scope outside
|
||||||
|
@ -4833,32 +4702,6 @@ function Ace2Inner(){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function bindEventHandler(target, type, func)
|
|
||||||
{
|
|
||||||
var handler;
|
|
||||||
if ((typeof func._wrapper) != "function")
|
|
||||||
{
|
|
||||||
func._wrapper = function(event)
|
|
||||||
{
|
|
||||||
func(fixEvent(event || window.event || {}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var handler = func._wrapper;
|
|
||||||
if (target.addEventListener) target.addEventListener(type, handler, false);
|
|
||||||
else target.attachEvent("on" + type, handler);
|
|
||||||
_teardownActions.push(function()
|
|
||||||
{
|
|
||||||
unbindEventHandler(target, type, func);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function unbindEventHandler(target, type, func)
|
|
||||||
{
|
|
||||||
var handler = func._wrapper;
|
|
||||||
if (target.removeEventListener) target.removeEventListener(type, handler, false);
|
|
||||||
else target.detachEvent("on" + type, handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSelectionPointX(point)
|
function getSelectionPointX(point)
|
||||||
{
|
{
|
||||||
// doesn't work in wrap-mode
|
// doesn't work in wrap-mode
|
||||||
|
@ -4992,27 +4835,30 @@ function Ace2Inner(){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var listAttributeName = 'list';
|
||||||
|
|
||||||
function getLineListType(lineNum)
|
function getLineListType(lineNum)
|
||||||
{
|
{
|
||||||
// get "list" attribute of first char of line
|
return documentAttributeManager.getAttributeOnLine(lineNum, listAttributeName)
|
||||||
var aline = rep.alines[lineNum];
|
|
||||||
if (aline)
|
|
||||||
{
|
|
||||||
var opIter = Changeset.opIterator(aline);
|
|
||||||
if (opIter.hasNext())
|
|
||||||
{
|
|
||||||
return Changeset.opAttributeValue(opIter.next(), 'list', rep.apool) || '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setLineListType(lineNum, listType)
|
function setLineListType(lineNum, listType)
|
||||||
{
|
{
|
||||||
setLineListTypes([
|
if(listType == ''){
|
||||||
[lineNum, listType]
|
documentAttributeManager.removeAttributeOnLine(lineNum, listAttributeName);
|
||||||
]);
|
}else{
|
||||||
|
documentAttributeManager.setAttributeOnLine(lineNum, listAttributeName, listType);
|
||||||
|
}
|
||||||
|
|
||||||
|
//if the list has been removed, it is necessary to renumber
|
||||||
|
//starting from the *next* line because the list may have been
|
||||||
|
//separated. If it returns null, it means that the list was not cut, try
|
||||||
|
//from the current one.
|
||||||
|
if(renumberList(lineNum+1)==null)
|
||||||
|
{
|
||||||
|
renumberList(lineNum);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renumberList(lineNum){
|
function renumberList(lineNum){
|
||||||
|
@ -5059,8 +4905,8 @@ function Ace2Inner(){
|
||||||
}
|
}
|
||||||
else if(curLevel == level)
|
else if(curLevel == level)
|
||||||
{
|
{
|
||||||
buildKeepRange(builder, loc, (loc = [line, 0]));
|
ChangesetUtils.buildKeepRange(rep, builder, loc, (loc = [line, 0]));
|
||||||
buildKeepRange(builder, loc, (loc = [line, 1]), [
|
ChangesetUtils.buildKeepRange(rep, builder, loc, (loc = [line, 1]), [
|
||||||
['start', position]
|
['start', position]
|
||||||
], rep.apool);
|
], rep.apool);
|
||||||
|
|
||||||
|
@ -5091,62 +4937,6 @@ function Ace2Inner(){
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setLineListTypes(lineNumTypePairsInOrder)
|
|
||||||
{
|
|
||||||
var loc = [0, 0];
|
|
||||||
var builder = Changeset.builder(rep.lines.totalWidth());
|
|
||||||
for (var i = 0; i < lineNumTypePairsInOrder.length; i++)
|
|
||||||
{
|
|
||||||
var pair = lineNumTypePairsInOrder[i];
|
|
||||||
var lineNum = pair[0];
|
|
||||||
var listType = pair[1];
|
|
||||||
buildKeepRange(builder, loc, (loc = [lineNum, 0]));
|
|
||||||
if (getLineListType(lineNum))
|
|
||||||
{
|
|
||||||
// already a line marker
|
|
||||||
if (listType)
|
|
||||||
{
|
|
||||||
// make different list type
|
|
||||||
buildKeepRange(builder, loc, (loc = [lineNum, 1]), [
|
|
||||||
['list', listType]
|
|
||||||
], rep.apool);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// remove list marker
|
|
||||||
buildRemoveRange(builder, loc, (loc = [lineNum, 1]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// currently no line marker
|
|
||||||
if (listType)
|
|
||||||
{
|
|
||||||
// add a line marker
|
|
||||||
builder.insert('*', [
|
|
||||||
['author', thisAuthor],
|
|
||||||
['insertorder', 'first'],
|
|
||||||
['list', listType]
|
|
||||||
], rep.apool);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var cs = builder.toString();
|
|
||||||
if (!Changeset.isIdentity(cs))
|
|
||||||
{
|
|
||||||
performDocumentApplyChangeset(cs);
|
|
||||||
}
|
|
||||||
|
|
||||||
//if the list has been removed, it is necessary to renumber
|
|
||||||
//starting from the *next* line because the list may have been
|
|
||||||
//separated. If it returns null, it means that the list was not cut, try
|
|
||||||
//from the current one.
|
|
||||||
if(renumberList(lineNum+1)==null)
|
|
||||||
{
|
|
||||||
renumberList(lineNum);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function doInsertList(type)
|
function doInsertList(type)
|
||||||
{
|
{
|
||||||
|
@ -5184,7 +4974,10 @@ function Ace2Inner(){
|
||||||
var t = getLineListType(n);
|
var t = getLineListType(n);
|
||||||
mods.push([n, allLinesAreList ? 'indent' + level : (t ? type + level : type + '1')]);
|
mods.push([n, allLinesAreList ? 'indent' + level : (t ? type + level : type + '1')]);
|
||||||
}
|
}
|
||||||
setLineListTypes(mods);
|
|
||||||
|
_.each(mods, function(mod){
|
||||||
|
setLineListType(mod[0], mod[1]);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function doInsertUnorderedList(){
|
function doInsertUnorderedList(){
|
||||||
|
@ -5520,67 +5313,7 @@ function Ace2Inner(){
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})());
|
})());
|
||||||
|
|
||||||
|
|
||||||
// stolen from jquery-1.2.1
|
|
||||||
|
|
||||||
|
|
||||||
function fixEvent(event)
|
|
||||||
{
|
|
||||||
// store a copy of the original event object
|
|
||||||
// and clone to set read-only properties
|
|
||||||
var originalEvent = event;
|
|
||||||
event = extend(
|
|
||||||
{}, originalEvent);
|
|
||||||
|
|
||||||
// add preventDefault and stopPropagation since
|
|
||||||
// they will not work on the clone
|
|
||||||
event.preventDefault = function()
|
|
||||||
{
|
|
||||||
// if preventDefault exists run it on the original event
|
|
||||||
if (originalEvent.preventDefault) originalEvent.preventDefault();
|
|
||||||
// otherwise set the returnValue property of the original event to false (IE)
|
|
||||||
originalEvent.returnValue = false;
|
|
||||||
};
|
|
||||||
event.stopPropagation = function()
|
|
||||||
{
|
|
||||||
// if stopPropagation exists run it on the original event
|
|
||||||
if (originalEvent.stopPropagation) originalEvent.stopPropagation();
|
|
||||||
// otherwise set the cancelBubble property of the original event to true (IE)
|
|
||||||
originalEvent.cancelBubble = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Fix target property, if necessary
|
|
||||||
if (!event.target && event.srcElement) event.target = event.srcElement;
|
|
||||||
|
|
||||||
// check if target is a textnode (safari)
|
|
||||||
if (browser.safari && event.target.nodeType == 3) event.target = originalEvent.target.parentNode;
|
|
||||||
|
|
||||||
// Add relatedTarget, if necessary
|
|
||||||
if (!event.relatedTarget && event.fromElement) event.relatedTarget = event.fromElement == event.target ? event.toElement : event.fromElement;
|
|
||||||
|
|
||||||
// Calculate pageX/Y if missing and clientX/Y available
|
|
||||||
if (event.pageX == null && event.clientX != null)
|
|
||||||
{
|
|
||||||
var e = document.documentElement,
|
|
||||||
b = document.body;
|
|
||||||
event.pageX = event.clientX + (e && e.scrollLeft || b.scrollLeft || 0);
|
|
||||||
event.pageY = event.clientY + (e && e.scrollTop || b.scrollTop || 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add which for key events
|
|
||||||
if (!event.which && (event.charCode || event.keyCode)) event.which = event.charCode || event.keyCode;
|
|
||||||
|
|
||||||
// Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
|
|
||||||
if (!event.metaKey && event.ctrlKey) event.metaKey = event.ctrlKey;
|
|
||||||
|
|
||||||
// Add which for click: 1 == left; 2 == middle; 3 == right
|
|
||||||
// Note: button is not normalized, so don't use it
|
|
||||||
if (!event.which && event.button) event.which = (event.button & 1 ? 1 : (event.button & 2 ? 3 : (event.button & 4 ? 2 : 0)));
|
|
||||||
|
|
||||||
return event;
|
|
||||||
}
|
|
||||||
|
|
||||||
var lineNumbersShown;
|
var lineNumbersShown;
|
||||||
var sideDivInner;
|
var sideDivInner;
|
||||||
|
|
||||||
|
@ -5664,7 +5397,68 @@ function Ace2Inner(){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Init documentAttributeManager
|
||||||
|
documentAttributeManager = new AttributeManager(rep, performDocumentApplyChangeset);
|
||||||
|
editorInfo.ace_performDocumentApplyAttributesToRange = documentAttributeManager.setAttributesOnRange;
|
||||||
|
|
||||||
|
$(document).ready(function(){
|
||||||
|
doc = document; // defined as a var in scope outside
|
||||||
|
inCallStack("setup", function()
|
||||||
|
{
|
||||||
|
var body = doc.getElementById("innerdocbody");
|
||||||
|
root = body; // defined as a var in scope outside
|
||||||
|
if (browser.mozilla) $(root).addClass("mozilla");
|
||||||
|
if (browser.safari) $(root).addClass("safari");
|
||||||
|
if (browser.msie) $(root).addClass("msie");
|
||||||
|
if (browser.msie)
|
||||||
|
{
|
||||||
|
// cache CSS background images
|
||||||
|
try
|
||||||
|
{
|
||||||
|
doc.execCommand("BackgroundImageCache", false, true);
|
||||||
|
}
|
||||||
|
catch (e)
|
||||||
|
{ /* throws an error in some IE 6 but not others! */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setClassPresence(root, "authorColors", true);
|
||||||
|
setClassPresence(root, "doesWrap", doesWrap);
|
||||||
|
|
||||||
|
initDynamicCSS();
|
||||||
|
|
||||||
|
enforceEditability();
|
||||||
|
|
||||||
|
// set up dom and rep
|
||||||
|
while (root.firstChild) root.removeChild(root.firstChild);
|
||||||
|
var oneEntry = createDomLineEntry("");
|
||||||
|
doRepLineSplice(0, rep.lines.length(), [oneEntry]);
|
||||||
|
insertDomLines(null, [oneEntry.domInfo], null);
|
||||||
|
rep.alines = Changeset.splitAttributionLines(
|
||||||
|
Changeset.makeAttribution("\n"), "\n");
|
||||||
|
|
||||||
|
bindTheEventHandlers();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
hooks.callAll('aceInitialized', {
|
||||||
|
editorInfo: editorInfo,
|
||||||
|
rep: rep,
|
||||||
|
documentAttributeManager: documentAttributeManager
|
||||||
|
});
|
||||||
|
|
||||||
|
scheduler.setTimeout(function()
|
||||||
|
{
|
||||||
|
parent.readyFunc(); // defined in code that sets up the inner iframe
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
isSetUp = true;
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.editor = new Ace2Inner();
|
// Ensure that plugins are loaded before initializing the editor
|
||||||
|
plugins.ensure(function () {
|
||||||
|
var editor = new Ace2Inner();
|
||||||
|
});
|
|
@ -20,16 +20,13 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var makeCSSManager = require('/cssmanager').makeCSSManager;
|
var makeCSSManager = require('./cssmanager').makeCSSManager;
|
||||||
var domline = require('/domline').domline;
|
var domline = require('./domline').domline;
|
||||||
var AttribPool = require('/AttributePoolFactory').createAttributePool;
|
var AttribPool = require('./AttributePool');
|
||||||
var Changeset = require('/Changeset');
|
var Changeset = require('./Changeset');
|
||||||
var linestylefilter = require('/linestylefilter').linestylefilter;
|
var linestylefilter = require('./linestylefilter').linestylefilter;
|
||||||
var colorutils = require('/colorutils').colorutils;
|
var colorutils = require('./colorutils').colorutils;
|
||||||
var Ace2Common = require('./ace2_common');
|
var _ = require('./underscore');
|
||||||
|
|
||||||
var map = Ace2Common.map;
|
|
||||||
var forEach = Ace2Common.forEach;
|
|
||||||
|
|
||||||
// These parameters were global, now they are injected. A reference to the
|
// These parameters were global, now they are injected. A reference to the
|
||||||
// Timeslider controller would probably be more appropriate.
|
// Timeslider controller would probably be more appropriate.
|
||||||
|
@ -155,7 +152,7 @@ function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro
|
||||||
// splice the lines
|
// splice the lines
|
||||||
splice: function(start, numRemoved, newLinesVA)
|
splice: function(start, numRemoved, newLinesVA)
|
||||||
{
|
{
|
||||||
var newLines = map(Array.prototype.slice.call(arguments, 2), function(s) {
|
var newLines = _.map(Array.prototype.slice.call(arguments, 2), function(s) {
|
||||||
return s;
|
return s;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -278,7 +275,7 @@ function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro
|
||||||
debugLog('Time Delta: ', timeDelta)
|
debugLog('Time Delta: ', timeDelta)
|
||||||
updateTimer();
|
updateTimer();
|
||||||
|
|
||||||
var authors = map(padContents.getActiveAuthors(), function(name)
|
var authors = _.map(padContents.getActiveAuthors(), function(name)
|
||||||
{
|
{
|
||||||
return authorData[name];
|
return authorData[name];
|
||||||
});
|
});
|
||||||
|
@ -384,7 +381,7 @@ function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro
|
||||||
changesetLoader.queueUp(start, 1, update);
|
changesetLoader.queueUp(start, 1, update);
|
||||||
}
|
}
|
||||||
|
|
||||||
var authors = map(padContents.getActiveAuthors(), function(name){
|
var authors = _.map(padContents.getActiveAuthors(), function(name){
|
||||||
return authorData[name];
|
return authorData[name];
|
||||||
});
|
});
|
||||||
BroadcastSlider.setAuthors(authors);
|
BroadcastSlider.setAuthors(authors);
|
||||||
|
@ -527,7 +524,7 @@ function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro
|
||||||
authorMap[obj.author] = obj.data;
|
authorMap[obj.author] = obj.data;
|
||||||
receiveAuthorData(authorMap);
|
receiveAuthorData(authorMap);
|
||||||
|
|
||||||
var authors = map(padContents.getActiveAuthors(),function(name) {
|
var authors = _.map(padContents.getActiveAuthors(), function(name) {
|
||||||
return authorData[name];
|
return authorData[name];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -607,10 +604,13 @@ function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro
|
||||||
setChannelState("DISCONNECTED", reason);
|
setChannelState("DISCONNECTED", reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Since its not used, import 'forEach' has been dropped
|
||||||
/*window['onloadFuncts'] = [];
|
/*window['onloadFuncts'] = [];
|
||||||
window.onload = function ()
|
window.onload = function ()
|
||||||
{
|
{
|
||||||
window['isloaded'] = true;
|
window['isloaded'] = true;
|
||||||
|
|
||||||
|
|
||||||
forEach(window['onloadFuncts'],function (funct)
|
forEach(window['onloadFuncts'],function (funct)
|
||||||
{
|
{
|
||||||
funct();
|
funct();
|
|
@ -22,7 +22,7 @@
|
||||||
|
|
||||||
// These parameters were global, now they are injected. A reference to the
|
// These parameters were global, now they are injected. A reference to the
|
||||||
// Timeslider controller would probably be more appropriate.
|
// Timeslider controller would probably be more appropriate.
|
||||||
var forEach = require('./ace2_common').forEach;
|
var _ = require('./underscore');
|
||||||
|
|
||||||
function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded)
|
function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded)
|
||||||
{
|
{
|
||||||
|
@ -170,41 +170,67 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded)
|
||||||
$('#error').show();
|
$('#error').show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var fixPadHeight = _.throttle(function(){
|
||||||
|
var height = $('#timeslider-top').height();
|
||||||
|
$('#editorcontainerbox').css({marginTop: height});
|
||||||
|
}, 600);
|
||||||
|
|
||||||
function setAuthors(authors)
|
function setAuthors(authors)
|
||||||
{
|
{
|
||||||
$("#authorstable").empty();
|
var authorsList = $("#authorsList");
|
||||||
|
authorsList.empty();
|
||||||
var numAnonymous = 0;
|
var numAnonymous = 0;
|
||||||
var numNamed = 0;
|
var numNamed = 0;
|
||||||
forEach(authors, function(author)
|
var colorsAnonymous = [];
|
||||||
|
_.each(authors, function(author)
|
||||||
{
|
{
|
||||||
|
var authorColor = clientVars.colorPalette[author.colorId] || author.colorId;
|
||||||
if (author.name)
|
if (author.name)
|
||||||
{
|
{
|
||||||
|
if (numNamed !== 0) authorsList.append(', ');
|
||||||
|
|
||||||
|
$('<span />')
|
||||||
|
.text(author.name || "unnamed")
|
||||||
|
.css('background-color', authorColor)
|
||||||
|
.addClass('author')
|
||||||
|
.appendTo(authorsList);
|
||||||
|
|
||||||
numNamed++;
|
numNamed++;
|
||||||
var tr = $('<tr></tr>');
|
|
||||||
var swatchtd = $('<td></td>');
|
|
||||||
var swatch = $('<div class="swatch"></div>');
|
|
||||||
swatch.css('background-color', clientVars.colorPalette[author.colorId]);
|
|
||||||
swatchtd.append(swatch);
|
|
||||||
tr.append(swatchtd);
|
|
||||||
var nametd = $('<td></td>');
|
|
||||||
nametd.text(author.name || "unnamed");
|
|
||||||
tr.append(nametd);
|
|
||||||
$("#authorstable").append(tr);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
numAnonymous++;
|
numAnonymous++;
|
||||||
|
if(authorColor) colorsAnonymous.push(authorColor);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (numAnonymous > 0)
|
if (numAnonymous > 0)
|
||||||
{
|
{
|
||||||
var html = "<tr><td colspan=\"2\" style=\"color:#999; padding-left: 10px\">" + (numNamed > 0 ? "...and " : "") + numAnonymous + " unnamed author" + (numAnonymous > 1 ? "s" : "") + "</td></tr>";
|
var anonymousAuthorString = numAnonymous + " unnamed author" + (numAnonymous > 1 ? "s" : "")
|
||||||
$("#authorstable").append($(html));
|
if (numNamed !== 0){
|
||||||
|
authorsList.append(' + ' + anonymousAuthorString);
|
||||||
|
} else {
|
||||||
|
authorsList.append(anonymousAuthorString);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(colorsAnonymous.length > 0){
|
||||||
|
authorsList.append(' (');
|
||||||
|
_.each(colorsAnonymous, function(color, i){
|
||||||
|
if( i > 0 ) authorsList.append(' ');
|
||||||
|
$('<span /> ')
|
||||||
|
.css('background-color', color)
|
||||||
|
.addClass('author author-anonymous')
|
||||||
|
.appendTo(authorsList);
|
||||||
|
});
|
||||||
|
authorsList.append(')');
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if (authors.length == 0)
|
if (authors.length == 0)
|
||||||
{
|
{
|
||||||
$("#authorstable").append($("<tr><td colspan=\"2\" style=\"color:#999; padding-left: 10px\">No Authors</td></tr>"))
|
authorsList.append("No Authors");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fixPadHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
BroadcastSlider = {
|
BroadcastSlider = {
|
||||||
|
@ -465,11 +491,10 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded)
|
||||||
{
|
{
|
||||||
if (clientVars.supportsSlider)
|
if (clientVars.supportsSlider)
|
||||||
{
|
{
|
||||||
$("#padmain, #rightbars").css('top', "130px");
|
|
||||||
$("#timeslider").show();
|
$("#timeslider").show();
|
||||||
setSliderLength(clientVars.totalRevs);
|
setSliderLength(clientVars.totalRevs);
|
||||||
setSliderPosition(clientVars.revNum);
|
setSliderPosition(clientVars.revNum);
|
||||||
forEach(clientVars.savedRevisions, function(revision)
|
_.each(clientVars.savedRevisions, function(revision)
|
||||||
{
|
{
|
||||||
addSavedRevision(revision.revNum, revision);
|
addSavedRevision(revision.revNum, revision);
|
||||||
})
|
})
|
|
@ -20,8 +20,8 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var AttribPool = require('/AttributePoolFactory').createAttributePool;
|
var AttributePool = require('./AttributePool');
|
||||||
var Changeset = require('/Changeset');
|
var Changeset = require('./Changeset');
|
||||||
|
|
||||||
function makeChangesetTracker(scheduler, apool, aceCallbacksProvider)
|
function makeChangesetTracker(scheduler, apool, aceCallbacksProvider)
|
||||||
{
|
{
|
||||||
|
@ -83,7 +83,7 @@ function makeChangesetTracker(scheduler, apool, aceCallbacksProvider)
|
||||||
baseAText = Changeset.cloneAText(atext);
|
baseAText = Changeset.cloneAText(atext);
|
||||||
if (apoolJsonObj)
|
if (apoolJsonObj)
|
||||||
{
|
{
|
||||||
var wireApool = (new AttribPool()).fromJsonable(apoolJsonObj);
|
var wireApool = (new AttributePool()).fromJsonable(apoolJsonObj);
|
||||||
baseAText.attribs = Changeset.moveOpsToNewPool(baseAText.attribs, wireApool, apool);
|
baseAText.attribs = Changeset.moveOpsToNewPool(baseAText.attribs, wireApool, apool);
|
||||||
}
|
}
|
||||||
submittedChangeset = null;
|
submittedChangeset = null;
|
||||||
|
@ -117,7 +117,7 @@ function makeChangesetTracker(scheduler, apool, aceCallbacksProvider)
|
||||||
|
|
||||||
if (apoolJsonObj)
|
if (apoolJsonObj)
|
||||||
{
|
{
|
||||||
var wireApool = (new AttribPool()).fromJsonable(apoolJsonObj);
|
var wireApool = (new AttributePool()).fromJsonable(apoolJsonObj);
|
||||||
c = Changeset.moveOpsToNewPool(c, wireApool, apool);
|
c = Changeset.moveOpsToNewPool(c, wireApool, apool);
|
||||||
}
|
}
|
||||||
|
|