From 254046454dcaddd8663deb1f51ceea2b5ee6319c Mon Sep 17 00:00:00 2001 From: Jean-Tiare Le Bigot Date: Sun, 8 Jan 2012 19:32:06 +0100 Subject: [PATCH] merge with upstream. Fixed confict in pad.html --- README.md | 4 +- bin/buildForWindows.sh | 2 +- bin/checkPad.js | 133 +++++++ bin/generateJsDoc.sh | 24 -- bin/installDeps.sh | 6 +- doc/jsdoc/README.md | 2 - doc/jsdoc/db/AuthorManager.md | 53 --- doc/jsdoc/db/DB.md | 20 - doc/jsdoc/db/Pad.md | 13 - doc/jsdoc/db/PadManager.md | 14 - doc/jsdoc/db/ReadOnlyManager.md | 21 - doc/jsdoc/easysync_tests.md | 7 - doc/jsdoc/handler/ExportHandler.md | 16 - doc/jsdoc/handler/ImportHandler.md | 15 - doc/jsdoc/handler/PadMessageHandler.md | 38 -- doc/jsdoc/handler/SocketIORouter.md | 23 -- doc/jsdoc/handler/TimesliderMessageHandler.md | 32 -- doc/jsdoc/server.md | 13 - doc/jsdoc/utils/Abiword.md | 15 - doc/jsdoc/utils/AttributePoolFactory.md | 15 - doc/jsdoc/utils/Changeset.md | 363 ------------------ doc/jsdoc/utils/ExportHtml.md | 24 -- doc/jsdoc/utils/Minify.md | 16 - doc/jsdoc/utils/Settings.md | 40 -- node/db/API.js | 154 +++----- node/db/AuthorManager.js | 28 +- node/db/GroupManager.js | 83 ++-- node/db/Pad.js | 39 +- node/db/PadManager.js | 67 +++- node/db/ReadOnlyManager.js | 4 +- node/db/SecurityManager.js | 26 +- node/db/SessionManager.js | 115 +++--- node/easysync_tests.js | 4 +- node/handler/APIHandler.js | 36 +- node/handler/ExportHandler.js | 62 ++- node/handler/ImportHandler.js | 19 +- node/handler/PadMessageHandler.js | 114 +++--- node/handler/SocketIORouter.js | 3 +- node/handler/TimesliderMessageHandler.js | 79 ++-- node/server.js | 183 +++++---- node/utils/Changeset.js | 6 + node/utils/ExportDokuWiki.js | 344 +++++++++++++++++ node/utils/ExportHtml.js | 16 +- node/utils/ImportHtml.js | 2 +- node/utils/Minify.js | 41 +- node/utils/Settings.js | 19 + node/utils/customError.js | 17 + package.json | 23 +- settings.json.template | 3 + static/css/pad.css | 114 +++++- static/css/timeslider.css | 5 + static/img/fileicons.gif | Bin 1397 -> 1649 bytes static/index.html | 52 ++- static/js/ace.js | 6 + static/js/ace2_common.js | 8 +- static/js/ace2_inner.js | 77 ++-- static/js/broadcast.js | 6 + static/js/broadcast_revisions.js | 6 + static/js/broadcast_slider.js | 6 + static/js/changesettracker.js | 6 + static/js/chat.js | 31 +- static/js/collab_client.js | 6 + static/js/colorutils.js | 6 + static/js/contentcollector.js | 6 + static/js/cssmanager.js | 6 + static/js/cssmanager_client.js | 6 + static/js/domline.js | 6 + static/js/domline_client.js | 6 + static/js/draggable.js | 6 + static/js/easysync2.js | 6 + static/js/easysync2_client.js | 6 + static/js/linestylefilter.js | 6 + static/js/linestylefilter_client.js | 6 + static/js/pad2.js | 34 +- static/js/pad_connectionstatus.js | 6 + static/js/pad_cookie.js | 6 + static/js/pad_docbar.js | 6 + static/js/pad_editbar.js | 8 +- static/js/pad_editor.js | 7 + static/js/pad_impexp.js | 38 +- static/js/pad_modals.js | 6 + static/js/pad_savedrevs.js | 6 + static/js/pad_userlist.js | 15 +- static/js/pad_utils.js | 6 + static/js/plugins.js | 6 + static/js/skiplist.js | 6 + static/js/undo-xpopup.js | 6 + static/js/undomodule.js | 6 + static/js/virtual_lines.js | 6 + static/pad.html | 39 +- static/timeslider.html | 45 ++- 91 files changed, 1674 insertions(+), 1363 deletions(-) create mode 100644 bin/checkPad.js delete mode 100755 bin/generateJsDoc.sh delete mode 100644 doc/jsdoc/README.md delete mode 100644 doc/jsdoc/db/AuthorManager.md delete mode 100644 doc/jsdoc/db/DB.md delete mode 100644 doc/jsdoc/db/Pad.md delete mode 100644 doc/jsdoc/db/PadManager.md delete mode 100644 doc/jsdoc/db/ReadOnlyManager.md delete mode 100644 doc/jsdoc/easysync_tests.md delete mode 100644 doc/jsdoc/handler/ExportHandler.md delete mode 100644 doc/jsdoc/handler/ImportHandler.md delete mode 100644 doc/jsdoc/handler/PadMessageHandler.md delete mode 100644 doc/jsdoc/handler/SocketIORouter.md delete mode 100644 doc/jsdoc/handler/TimesliderMessageHandler.md delete mode 100644 doc/jsdoc/server.md delete mode 100644 doc/jsdoc/utils/Abiword.md delete mode 100644 doc/jsdoc/utils/AttributePoolFactory.md delete mode 100644 doc/jsdoc/utils/Changeset.md delete mode 100644 doc/jsdoc/utils/ExportHtml.md delete mode 100644 doc/jsdoc/utils/Minify.md delete mode 100644 doc/jsdoc/utils/Settings.md create mode 100644 node/utils/ExportDokuWiki.js create mode 100644 node/utils/customError.js diff --git a/README.md b/README.md index b21cd8e82..ab241333f 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ Here is the **[FAQ](https://github.com/Pita/etherpad-lite/wiki/FAQ)** **As any user (we recommend creating a separate user called etherpad-lite):** -
    +
    1. Move to a folder where you want to install Etherpad Lite. Clone the git repository git clone 'git://github.com/Pita/etherpad-lite.git'
       
    2. Install the dependencies with bin/installDeps.sh
       
    3. Start it with bin/run.sh
       
    4. @@ -114,8 +114,8 @@ You also help the project, if you only host a Etherpad Lite instance and share y # Modules created for this project * [ueberDB](https://github.com/Pita/ueberDB) "transforms every database into a object key value store" - manages all database access -* [doc.md](https://github.com/Pita/doc.md) "A simple JSDoc documentation tool that creates markdown for node.js modules exports" - is used to generate the docs * [channels](https://github.com/Pita/channels) "Event channels in node.js" - ensures that ueberDB operations are atomic and in series for each key +* [async-stacktrace](https://github.com/Pita/async-stacktrace) "Improves node.js stacktraces and makes it easier to handle errors" # License [Apache License v2](http://www.apache.org/licenses/LICENSE-2.0.html) diff --git a/bin/buildForWindows.sh b/bin/buildForWindows.sh index 8e6ce4f6e..6e46e29a1 100755 --- a/bin/buildForWindows.sh +++ b/bin/buildForWindows.sh @@ -1,6 +1,6 @@ #!/bin/sh -NODE_VERSION="0.6.1" +NODE_VERSION="0.6.5" #Move to the folder where ep-lite is installed cd `dirname $0` diff --git a/bin/checkPad.js b/bin/checkPad.js new file mode 100644 index 000000000..9e0544415 --- /dev/null +++ b/bin/checkPad.js @@ -0,0 +1,133 @@ +/* + This is a debug tool. It checks all revisions for data corruption +*/ + +if(process.argv.length != 3) +{ + console.error("Use: node checkPad.js $PADID"); + process.exit(1); +} +//get the padID +var padId = process.argv[2]; + +//initalize the database +var log4js = require("log4js"); +log4js.setGlobalLogLevel("INFO"); +var async = require("async"); +var db = require('../node/db/DB'); +var Changeset = require('../node/utils/Changeset'); +var padManager; + +async.series([ + //intallize the database + function (callback) + { + db.init(callback); + }, + //get the pad + function (callback) + { + padManager = require('../node/db/PadManager'); + + padManager.doesPadExists(padId, function(err, exists) + { + if(!exists) + { + console.error("Pad does not exist"); + process.exit(1); + } + + padManager.getPad(padId, function(err, _pad) + { + pad = _pad; + callback(err); + }); + }); + }, + function (callback) + { + //create an array with key kevisions + //key revisions always save the full pad atext + var head = pad.getHeadRevisionNumber(); + var keyRevisions = []; + for(var i=0;i /dev/null 2>&1 || { - echo "You need to install node!" >&2 - exit 1 -} - -hash doc.md > /dev/null 2>&1 || { - echo "You need to install doc.md! npm install -g doc.md" >&2 - exit 1 -} - -echo "empty doc folder..." -rm -rf doc/jsdoc/* - -doc.md node doc/jsdoc diff --git a/bin/installDeps.sh b/bin/installDeps.sh index 088260853..0533caf2d 100755 --- a/bin/installDeps.sh +++ b/bin/installDeps.sh @@ -16,7 +16,7 @@ hash curl > /dev/null 2>&1 || { #Is node installed? hash node > /dev/null 2>&1 || { - echo "Please install node.js ( http://nodesjs.org )" >&2 + echo "Please install node.js ( http://nodejs.org )" >&2 exit 1 } @@ -28,8 +28,8 @@ hash npm > /dev/null 2>&1 || { #check npm version NPM_VERSION=$(npm --version) -if [ ! $(echo $NPM_VERSION | cut -d "." -f 1-2) = "1.0" ]; then - echo "You're running a wrong version of npm, you're using $NPM_VERSION, we need 1.0.x" >&2 +if [ ! $(echo $NPM_VERSION | cut -d "." -f 1) = "1" ]; then + echo "You're running a wrong version of npm, you're using $NPM_VERSION, we need 1.x" >&2 exit 1 fi diff --git a/doc/jsdoc/README.md b/doc/jsdoc/README.md deleted file mode 100644 index 066f3731d..000000000 --- a/doc/jsdoc/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# About this folder -This is the documentation generated with doc.md. This documentation might be not up to date, you can generate the latest jsdocs with bin/generateJsDoc.sh \ No newline at end of file diff --git a/doc/jsdoc/db/AuthorManager.md b/doc/jsdoc/db/AuthorManager.md deleted file mode 100644 index 257d112c9..000000000 --- a/doc/jsdoc/db/AuthorManager.md +++ /dev/null @@ -1,53 +0,0 @@ -# db/AuthorMana -`require("./db/AuthorManager");` - -The AuthorManager controlls all information about the Pad authors - -## Functions - -- - - -### getAuthor (author, callback) -Internal function that creates the database entry for an author - -* **author** *(String)* The id of the author -* **callback** *(Function)* callback(err, authorObj) - -- - - -### getAuthor4Token (token, callback) -Returns the Author Id for a token. If the token is unkown, -it creates a author for the token - -* **token** *(String)* The token -* **callback** *(Function)* callback (err, author) -The callback function that is called when the result is here - -- - - -### getAuthorColorId (author, callback) -Returns the color Id of the author - -* **author** *(String)* The id of the author -* **callback** *(Function)* callback(err, colorId) - -- - - -### getAuthorName (author, callback) -Returns the name of the author - -* **author** *(String)* The id of the author -* **callback** *(Function)* callback(err, name) - -- - - -### setAuthorColorId (author, colorId, callback) -Sets the color Id of the author - -* **author** *(String)* The id of the author -* **colorId** *No description* -* **callback** *(Function)* (optional) - -- - - -### setAuthorName (author, name, callback) -Sets the name of the author - -* **author** *(String)* The id of the author -* **name** *No description* -* **callback** *(Function)* (optional) - diff --git a/doc/jsdoc/db/DB.md b/doc/jsdoc/db/DB.md deleted file mode 100644 index 6acfd826c..000000000 --- a/doc/jsdoc/db/DB.md +++ /dev/null @@ -1,20 +0,0 @@ -# db -`require("./db/DB");` - -The DB Module provides a database initalized with the settings -provided by the settings module - -## Functions - -- - - -### init (callback) -Initalizes the database with the settings provided by the settings module - -* **callback** *(Function)* *No description* - -##Variables - -- - - -### db -The UeberDB Object that provides the database functions - diff --git a/doc/jsdoc/db/Pad.md b/doc/jsdoc/db/Pad.md deleted file mode 100644 index 54b122b7d..000000000 --- a/doc/jsdoc/db/Pad.md +++ /dev/null @@ -1,13 +0,0 @@ -# db/ -`require("./db/Pad");` - -The pad object, defined with joose - -## Functions - -- - - -### cleanText (txt) -Copied from the Etherpad source code. It converts Windows line breaks to Unix line breaks and convert Tabs to spaces - -* **txt** *No description* - diff --git a/doc/jsdoc/db/PadManager.md b/doc/jsdoc/db/PadManager.md deleted file mode 100644 index 7721b3e86..000000000 --- a/doc/jsdoc/db/PadManager.md +++ /dev/null @@ -1,14 +0,0 @@ -# db/PadMana -`require("./db/PadManager");` - -The Pad Manager is a Factory for pad Objects - -## Functions - -- - - -### getPad (id, callback) -A Array with all known Pads - -* **id** A String with the id of the pad -* **callback** *(Function)* *No description* - diff --git a/doc/jsdoc/db/ReadOnlyManager.md b/doc/jsdoc/db/ReadOnlyManager.md deleted file mode 100644 index 4b6fc8afd..000000000 --- a/doc/jsdoc/db/ReadOnlyManager.md +++ /dev/null @@ -1,21 +0,0 @@ -# db/ReadOnlyMana -`require("./db/ReadOnlyManager");` - -The ReadOnlyManager manages the database and rendering releated to read only pads - -## Functions - -- - - -### getPadId (readOnlyId, callback) -returns a the padId for a read only id - -* **readOnlyId** *(String)* read only id -* **callback** *No description* - -- - - -### getReadOnlyId (padId, callback) -returns a read only id for a pad - -* **padId** *(String)* the id of the pad -* **callback** *No description* - diff --git a/doc/jsdoc/easysync_tests.md b/doc/jsdoc/easysync_tests.md deleted file mode 100644 index 0a9a7b448..000000000 --- a/doc/jsdoc/easysync_tests.md +++ /dev/null @@ -1,7 +0,0 @@ -# easysync_tests -`require("./easysync_tests");` - -I found this tests in the old Etherpad and used it to test if the Changeset library can be run on node.js. -It has no use for ep-lite, but I thought I keep it cause it may help someone to understand the Changeset library -https://github.com/ether/pad/blob/master/infrastructure/ace/www/easysync2_tests.js - diff --git a/doc/jsdoc/handler/ExportHandler.md b/doc/jsdoc/handler/ExportHandler.md deleted file mode 100644 index 7117715f7..000000000 --- a/doc/jsdoc/handler/ExportHandler.md +++ /dev/null @@ -1,16 +0,0 @@ -# handler/Expor -`require("./handler/ExportHandler");` - -Handles the export requests - -## Functions - -- - - -### doExport (req, res, padId, type) -do a requested export - -* **req** *No description* -* **res** *No description* -* **padId** *No description* -* **type** *No description* - diff --git a/doc/jsdoc/handler/ImportHandler.md b/doc/jsdoc/handler/ImportHandler.md deleted file mode 100644 index e255586f1..000000000 --- a/doc/jsdoc/handler/ImportHandler.md +++ /dev/null @@ -1,15 +0,0 @@ -# handler/Impor -`require("./handler/ImportHandler");` - -Handles the import requests - -## Functions - -- - - -### doImport (req, res, padId) -do a requested import - -* **req** *No description* -* **res** *No description* -* **padId** *No description* - diff --git a/doc/jsdoc/handler/PadMessageHandler.md b/doc/jsdoc/handler/PadMessageHandler.md deleted file mode 100644 index a17df95fd..000000000 --- a/doc/jsdoc/handler/PadMessageHandler.md +++ /dev/null @@ -1,38 +0,0 @@ -# handler/PadMessag -`require("./handler/PadMessageHandler");` - -The MessageHandler handles all Messages that comes from Socket.IO and controls the sessions - -## Functions - -- - - -### handleConnect (client) -Handles the connection of a new user - -* **client** the new client - -- - - -### handleDisconnect (client) -Handles the disconnection of a user - -* **client** the client that leaves - -- - - -### handleMessage (client, message) -Handles a message from a user - -* **client** the client that send this message -* **message** the message from the client - -- - - -### setSocketIO (socket_io) -A associative array that translates a session to a pad - -* **socket_io** The Socket - -- - - -### updatePadClients (pad, callback) - -* **pad** *No description* -* **callback** *No description* - diff --git a/doc/jsdoc/handler/SocketIORouter.md b/doc/jsdoc/handler/SocketIORouter.md deleted file mode 100644 index 8effc96d6..000000000 --- a/doc/jsdoc/handler/SocketIORouter.md +++ /dev/null @@ -1,23 +0,0 @@ -# handler/Socket -`require("./handler/SocketIORouter");` - -This is the Socket.IO Router. It routes the Messages between the -components of the Server. The components are at the moment: pad and timeslider - -## Functions - -- - - -### addComponent (moduleName, module) -Saves all components -key is the component name -value is the component module - -* **moduleName** *No description* -* **module** *No description* - -- - - -### setSocketIO (_socket) -sets the socket.io and adds event functions for routing - -* **_socket** *No description* - diff --git a/doc/jsdoc/handler/TimesliderMessageHandler.md b/doc/jsdoc/handler/TimesliderMessageHandler.md deleted file mode 100644 index ac1edac62..000000000 --- a/doc/jsdoc/handler/TimesliderMessageHandler.md +++ /dev/null @@ -1,32 +0,0 @@ -# handler/TimesliderMessag -`require("./handler/TimesliderMessageHandler");` - -The MessageHandler handles all Messages that comes from Socket.IO and controls the sessions - -## Functions - -- - - -### handleConnect (client) -Handles the connection of a new user - -* **client** the new client - -- - - -### handleDisconnect (client) -Handles the disconnection of a user - -* **client** the client that leaves - -- - - -### handleMessage (client, message) -Handles a message from a user - -* **client** the client that send this message -* **message** the message from the client - -- - - -### setSocketIO (socket_io) -Saves the Socket class we need to send and recieve data from the client - -* **socket_io** The Socket - diff --git a/doc/jsdoc/server.md b/doc/jsdoc/server.md deleted file mode 100644 index 90b37486c..000000000 --- a/doc/jsdoc/server.md +++ /dev/null @@ -1,13 +0,0 @@ -# server -`require("./server");` - -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. - -##Variables - -- - - -### maxAge - - diff --git a/doc/jsdoc/utils/Abiword.md b/doc/jsdoc/utils/Abiword.md deleted file mode 100644 index 680e24992..000000000 --- a/doc/jsdoc/utils/Abiword.md +++ /dev/null @@ -1,15 +0,0 @@ -# utils/A -`require("./utils/Abiword");` - -Controls the communication with the Abiword application - -## Functions - -- - - -### convertFile (srcFile, destFile, type, callback) - -* **srcFile** *No description* -* **destFile** *No description* -* **type** *No description* -* **callback** *No description* - diff --git a/doc/jsdoc/utils/AttributePoolFactory.md b/doc/jsdoc/utils/AttributePoolFactory.md deleted file mode 100644 index 4f52fcc3f..000000000 --- a/doc/jsdoc/utils/AttributePoolFactory.md +++ /dev/null @@ -1,15 +0,0 @@ -# utils/AttributePoolF -`require("./utils/AttributePoolFactory");` - -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 - -## Functions - -- - - -### createAttributePool () - - diff --git a/doc/jsdoc/utils/Changeset.md b/doc/jsdoc/utils/Changeset.md deleted file mode 100644 index 6f08514a2..000000000 --- a/doc/jsdoc/utils/Changeset.md +++ /dev/null @@ -1,363 +0,0 @@ -# utils/Cha -`require("./utils/Changeset");` - -## Functions - -- - - -### _slicerZipperFunc (attOp, csOp, opOut, pool) - -* **attOp** *No description* -* **csOp** *No description* -* **opOut** *No description* -* **pool** *No description* - -- - - -### appendATextToAssembler (atext, assem) - -* **atext** *No description* -* **assem** *No description* - -- - - -### applyToAText (cs, atext, pool) - -* **cs** *No description* -* **atext** *No description* -* **pool** *No description* - -- - - -### applyToAttribution (cs, astr, pool) - -* **cs** *No description* -* **astr** *No description* -* **pool** *No description* - -- - - -### applyToText (cs, str) - -* **cs** *No description* -* **str** *No description* - -- - - -### applyZip (in1, idx1, in2, idx2, func) - -* **in1** *No description* -* **idx1** *No description* -* **in2** *No description* -* **idx2** *No description* -* **func** *No description* - -- - - -### attribsAttributeValue (attribs, key, pool) - -* **attribs** *No description* -* **key** *No description* -* **pool** *No description* - -- - - -### attributeTester (attribPair, pool) - -* **attribPair** *No description* -* **pool** *No description* - -- - - -### builder (oldLen) - -* **oldLen** *No description* - -- - - -### characterRangeFollow (cs, startChar, endChar, insertionsAfter) - -* **cs** *No description* -* **startChar** *No description* -* **endChar** *No description* -* **insertionsAfter** *No description* - -- - - -### checkRep (cs) - -* **cs** *No description* - -- - - -### clearOp (op) - -* **op** *No description* - -- - - -### cloneAText (atext) - -* **atext** *No description* - -- - - -### cloneOp (op) - -* **op** *No description* - -- - - -### compose (cs1, cs2, pool) - -* **cs1** *No description* -* **cs2** *No description* -* **pool** *No description* - -- - - -### composeAttributes (att1, att2, resultIsMutation, pool) - -* **att1** *No description* -* **att2** *No description* -* **resultIsMutation** *No description* -* **pool** *No description* - -- - - -### copyAText (atext1, atext2) - -* **atext1** *No description* -* **atext2** *No description* - -- - - -### copyOp (op1, op2) - -* **op1** *No description* -* **op2** *No description* - -- - - -### eachAttribNumber (cs, func) - -* **cs** *No description* -* **func** *No description* - -- - - -### filterAttribNumbers (cs, filter) - -* **cs** *No description* -* **filter** *No description* - -- - - -### follow (cs1, cs2, reverseInsertOrder, pool) - -* **cs1** *No description* -* **cs2** *No description* -* **reverseInsertOrder** *No description* -* **pool** *No description* - -- - - -### followAttributes (att1, att2, pool) - -* **att1** *No description* -* **att2** *No description* -* **pool** *No description* - -- - - -### identity (N) - -* **N** *No description* - -- - - -### inverse (cs, lines, alines, pool) - -* **cs** *No description* -* **lines** *No description* -* **alines** *No description* -* **pool** *No description* - -- - - -### isIdentity (cs) - -* **cs** *No description* - -- - - -### joinAttributionLines (theAlines) - -* **theAlines** *No description* - -- - - -### makeAText (text, attribs) - -* **text** *No description* -* **attribs** *No description* - -- - - -### makeAttribsString (opcode, attribs, pool) - -* **opcode** *No description* -* **attribs** *No description* -* **pool** *No description* - -- - - -### makeAttribution (text) - -* **text** *No description* - -- - - -### makeSplice (oldFullText, spliceStart, numRemoved, newText, optNewTextAPairs, pool) - -* **oldFullText** *No description* -* **spliceStart** *No description* -* **numRemoved** *No description* -* **newText** *No description* -* **optNewTextAPairs** *No description* -* **pool** *No description* - -- - - -### mapAttribNumbers (cs, func) - -* **cs** *No description* -* **func** *No description* - -- - - -### mergingOpAssembler () - - -- - - -### moveOpsToNewPool (cs, oldPool, newPool) - -* **cs** *No description* -* **oldPool** *No description* -* **newPool** *No description* - -- - - -### mutateAttributionLines (cs, lines, pool) - -* **cs** *No description* -* **lines** *No description* -* **pool** *No description* - -- - - -### mutateTextLines (cs, lines) - -* **cs** *No description* -* **lines** *No description* - -- - - -### newLen (cs) - -* **cs** *No description* - -- - - -### newOp (optOpcode) - -* **optOpcode** *No description* - -- - - -### numToString (num) - -* **num** *No description* - -- - - -### oldLen (cs) - -* **cs** *No description* - -- - - -### oneInsertedLineAtATimeOpIterator (opsStr, optStartIndex, charBank) - -* **opsStr** *No description* -* **optStartIndex** *No description* -* **charBank** *No description* - -- - - -### opAssembler () - - -- - - -### opAttributeValue (op, key, pool) - -* **op** *No description* -* **key** *No description* -* **pool** *No description* - -- - - -### opIterator (opsStr, optStartIndex) - -* **opsStr** *No description* -* **optStartIndex** *No description* - -- - - -### opString (op) - -* **op** *No description* - -- - - -### pack (oldLen, newLen, opsStr, bank) - -* **oldLen** *No description* -* **newLen** *No description* -* **opsStr** *No description* -* **bank** *No description* - -- - - -### parseNum (str) - -* **str** *No description* - -- - - -### prepareForWire (cs, pool) - -* **cs** *No description* -* **pool** *No description* - -- - - -### smartOpAssembler () - - -- - - -### splitAttributionLines (attrOps, text) - -* **attrOps** *No description* -* **text** *No description* - -- - - -### splitTextLines (text) - -* **text** *No description* - -- - - -### stringAssembler () - - -- - - -### stringIterator (str) - -* **str** *No description* - -- - - -### stringOp (str) - -* **str** *No description* - -- - - -### subattribution (astr, start, optEnd) - -* **astr** *No description* -* **start** *No description* -* **optEnd** *No description* - -- - - -### textLinesMutator (lines) - -* **lines** *No description* - -- - - -### toBaseTen (cs) - -* **cs** *No description* - -- - - -### toSplices (cs) - -* **cs** *No description* - -- - - -### unpack (cs) - -* **cs** *No description* - -##Variables - -- - - -### assert - - -- - - -### error - - diff --git a/doc/jsdoc/utils/ExportHtml.md b/doc/jsdoc/utils/ExportHtml.md deleted file mode 100644 index 8c56abbc4..000000000 --- a/doc/jsdoc/utils/ExportHtml.md +++ /dev/null @@ -1,24 +0,0 @@ -# utils/Expo -`require("./utils/ExportHtml");` - -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. - -## Functions - -- - - -### getPadHTMLDocument (padId, revNum, noDocType, callback) - -* **padId** *No description* -* **revNum** *No description* -* **noDocType** *No description* -* **callback** *No description* - diff --git a/doc/jsdoc/utils/Minify.md b/doc/jsdoc/utils/Minify.md deleted file mode 100644 index b2d1cba31..000000000 --- a/doc/jsdoc/utils/Minify.md +++ /dev/null @@ -1,16 +0,0 @@ -# utils/ -`require("./utils/Minify");` - -This Module manages all /minified/* requests. It controls the -minification && compression of Javascript and CSS. - -## Functions - -- - - -### minifyJS (req, res, jsFilename) -creates the minifed javascript for the given minified name - -* **req** the Express request -* **res** the Express response -* **jsFilename** *No description* - diff --git a/doc/jsdoc/utils/Settings.md b/doc/jsdoc/utils/Settings.md deleted file mode 100644 index 1750c4382..000000000 --- a/doc/jsdoc/utils/Settings.md +++ /dev/null @@ -1,40 +0,0 @@ -# utils/Se -`require("./utils/Settings");` - -The Settings Modul reads the settings out of settings.json and provides -this information to the other modules - -##Variables - -- - - -### abiword -The path of the abiword executable - -- - - -### dbSettings -This setting is passed with dbType to ueberDB to set up the database - -- - - -### dbType - - -- - - -### defaultPadText -The default Text of a new pad - -- - - -### ip -The IP ep-lite should listen to - -- - - -### logHTTP -A flag that shows if http requests should be loged to stdout - -- - - -### minify -A flag that shows if minification is enabled or not - -- - - -### port -The Port ep-lite should listen to - diff --git a/node/db/API.js b/node/db/API.js index c40c49cac..09cc95afc 100644 --- a/node/db/API.js +++ b/node/db/API.js @@ -18,6 +18,8 @@ * limitations under the License. */ +var ERR = require("async-stacktrace"); +var customError = require("../utils/customError"); var padManager = require("./PadManager"); var padMessageHandler = require("../handler/PadMessageHandler"); var readOnlyManager = require("./ReadOnlyManager"); @@ -87,7 +89,7 @@ exports.getText = function(padID, rev, callback) } else { - callback({stop: "rev is not a number"}); + callback(new customError("rev is not a number", "apierror")); return; } } @@ -95,25 +97,21 @@ exports.getText = function(padID, rev, callback) //ensure this is not a negativ number if(rev !== undefined && rev < 0) { - callback({stop: "rev is a negativ number"}); + callback(new customError("rev is a negativ number","apierror")); return; } //ensure this is not a float value if(rev !== undefined && !is_int(rev)) { - callback({stop: "rev is a float value"}); + callback(new customError("rev is a float value","apierror")); return; } //get the pad getPadSafe(padID, true, function(err, pad) { - if(err) - { - callback(err); - return; - } + if(ERR(err, callback)) return; //the client asked for a special revision if(rev !== undefined) @@ -121,19 +119,18 @@ exports.getText = function(padID, rev, callback) //check if this is a valid revision if(rev > pad.getHeadRevisionNumber()) { - callback({stop: "rev is higher than the head revision of the pad"}); + callback(new customError("rev is higher than the head revision of the pad","apierror")); return; } //get the text of this revision pad.getInternalRevisionAText(rev, function(err, atext) { - if(!err) - { - data = {text: atext.text}; - } + if(ERR(err, callback)) return; - callback(err, data); + data = {text: atext.text}; + + callback(null, data); }) } //the client wants the latest text, lets return it to him @@ -158,11 +155,7 @@ exports.setText = function(padID, text, callback) //get the pad getPadSafe(padID, true, function(err, pad) { - if(err) - { - callback(err); - return; - } + if(ERR(err, callback)) return; //set the text pad.setText(text); @@ -196,30 +189,26 @@ exports.getHTML = function(padID, rev, callback) } else { - callback({stop: "rev is not a number"}); + callback(new customError("rev is not a number","apierror")); return; } } if(rev !== undefined && rev < 0) { - callback({stop: "rev is a negative number"}); + callback(new customError("rev is a negative number","apierror")); return; } if(rev !== undefined && !is_int(rev)) { - callback({stop: "rev is a float value"}); + callback(new customError("rev is a float value","apierror")); return; } getPadSafe(padID, true, function(err, pad) { - if(err) - { - callback(err); - return; - } + if(ERR(err, callback)) return; //the client asked for a special revision if(rev !== undefined) @@ -227,18 +216,16 @@ exports.getHTML = function(padID, rev, callback) //check if this is a valid revision if(rev > pad.getHeadRevisionNumber()) { - callback({stop: "rev is higher than the head revision of the pad"}); + callback(new customError("rev is higher than the head revision of the pad","apierror")); return; } //get the html of this revision exportHtml.getPadHTML(pad, rev, function(err, html) { - if(!err) - { - data = {html: html}; - } - callback(err, data); + if(ERR(err, callback)) return; + data = {html: html}; + callback(null, data); }); } //the client wants the latest text, lets return it to him @@ -246,11 +233,11 @@ exports.getHTML = function(padID, rev, callback) { exportHtml.getPadHTML(pad, undefined, function (err, html) { - if(!err) - { - data = {html: html}; - } - callback(err, data); + if(ERR(err, callback)) return; + + data = {html: html}; + + callback(null, data); }); } }); @@ -261,11 +248,7 @@ exports.setHTML = function(padID, html, callback) //get the pad getPadSafe(padID, true, function(err, pad) { - if(err) - { - callback(err); - return; - } + if(ERR(err, callback)) return; // add a new changeset with the new html to the pad importHtml.setPadHTML(pad, cleanText(html)); @@ -293,11 +276,7 @@ exports.getRevisionsCount = function(padID, callback) //get the pad getPadSafe(padID, true, function(err, pad) { - if(err) - { - callback(err); - return; - } + if(ERR(err, callback)) return; callback(null, {revisions: pad.getHeadRevisionNumber()}); }); @@ -314,16 +293,17 @@ Example returns: exports.createPad = function(padID, text, callback) { //ensure there is no $ in the padID - if(padID.indexOf("$") != -1) + if(padID && padID.indexOf("$") != -1) { - callback({stop: "createPad can't create group pads"}); + callback(new customError("createPad can't create group pads","apierror")); return; } //create pad getPadSafe(padID, false, text, function(err) { - callback(err); + if(ERR(err, callback)) return; + callback(); }); } @@ -339,11 +319,7 @@ exports.deletePad = function(padID, callback) { getPadSafe(padID, true, function(err, pad) { - if(err) - { - callback(err); - return; - } + if(ERR(err, callback)) return; pad.remove(callback); }); @@ -362,16 +338,13 @@ exports.getReadOnlyID = function(padID, callback) //we don't need the pad object, but this function does all the security stuff for us getPadSafe(padID, true, function(err) { - if(err) - { - callback(err); - return; - } + if(ERR(err, callback)) return; //get the readonlyId readOnlyManager.getReadOnlyId(padID, function(err, readOnlyId) { - callback(err, {readOnlyID: readOnlyId}); + if(ERR(err, callback)) return; + callback(null, {readOnlyID: readOnlyId}); }); }); } @@ -387,20 +360,16 @@ Example returns: exports.setPublicStatus = function(padID, publicStatus, callback) { //ensure this is a group pad - if(padID.indexOf("$") == -1) + if(padID && padID.indexOf("$") == -1) { - callback({stop: "You can only get/set the publicStatus of pads that belong to a group"}); + callback(new customError("You can only get/set the publicStatus of pads that belong to a group","apierror")); return; } //get the pad getPadSafe(padID, true, function(err, pad) { - if(err) - { - callback(err); - return; - } + if(ERR(err, callback)) return; //convert string to boolean if(typeof publicStatus == "string") @@ -424,20 +393,16 @@ Example returns: exports.getPublicStatus = function(padID, callback) { //ensure this is a group pad - if(padID.indexOf("$") == -1) + if(padID && padID.indexOf("$") == -1) { - callback({stop: "You can only get/set the publicStatus of pads that belong to a group"}); + callback(new customError("You can only get/set the publicStatus of pads that belong to a group","apierror")); return; } //get the pad getPadSafe(padID, true, function(err, pad) { - if(err) - { - callback(err); - return; - } + if(ERR(err, callback)) return; callback(null, {publicStatus: pad.getPublicStatus()}); }); @@ -454,20 +419,16 @@ Example returns: exports.setPassword = function(padID, password, callback) { //ensure this is a group pad - if(padID.indexOf("$") == -1) + if(padID && padID.indexOf("$") == -1) { - callback({stop: "You can only get/set the password of pads that belong to a group"}); + callback(new customError("You can only get/set the password of pads that belong to a group","apierror")); return; } //get the pad getPadSafe(padID, true, function(err, pad) { - if(err) - { - callback(err); - return; - } + if(ERR(err, callback)) return; //set the password pad.setPassword(password); @@ -487,20 +448,16 @@ Example returns: exports.isPasswordProtected = function(padID, callback) { //ensure this is a group pad - if(padID.indexOf("$") == -1) + if(padID && padID.indexOf("$") == -1) { - callback({stop: "You can only get/set the password of pads that belong to a group"}); + callback(new customError("You can only get/set the password of pads that belong to a group","apierror")); return; } //get the pad getPadSafe(padID, true, function(err, pad) { - if(err) - { - callback(err); - return; - } + if(ERR(err, callback)) return; callback(null, {isPasswordProtected: pad.isPasswordProtected()}); }); @@ -528,34 +485,31 @@ function getPadSafe(padID, shouldExist, text, callback) //check if padID is a string if(typeof padID != "string") { - callback({stop: "padID is not a string"}); + callback(new customError("padID is not a string","apierror")); return; } //check if the padID maches the requirements if(!padManager.isValidPadId(padID)) { - callback({stop: "padID did not match requirements"}); + callback(new customError("padID did not match requirements","apierror")); return; } //check if the pad exists padManager.doesPadExists(padID, function(err, exists) { - //error - if(err) - { - callback(err); - } + if(ERR(err, callback)) return; + //does not exist, but should - else if(exists == false && shouldExist == true) + if(exists == false && shouldExist == true) { - callback({stop: "padID does not exist"}); + callback(new customError("padID does not exist","apierror")); } //does exists, but shouldn't else if(exists == true && shouldExist == false) { - callback({stop: "padID does already exist"}); + callback(new customError("padID does already exist","apierror")); } //pad exists, let's get it else diff --git a/node/db/AuthorManager.js b/node/db/AuthorManager.js index 958e2c16c..f4f42d112 100644 --- a/node/db/AuthorManager.js +++ b/node/db/AuthorManager.js @@ -18,6 +18,7 @@ * limitations under the License. */ +var ERR = require("async-stacktrace"); var db = require("./DB").db; var async = require("async"); @@ -29,7 +30,8 @@ exports.doesAuthorExists = function (authorID, callback) //check if the database entry of this author exists db.get("globalAuthor:" + authorID, function (err, author) { - callback(err, author != null); + if(ERR(err, callback)) return; + callback(null, author != null); }); } @@ -42,8 +44,9 @@ exports.getAuthor4Token = function (token, callback) { mapAuthorWithDBKey("token2author", token, function(err, author) { + if(ERR(err, callback)) return; //return only the sub value authorID - callback(err, author ? author.authorID : author); + callback(null, author ? author.authorID : author); }); } @@ -56,12 +59,7 @@ exports.createAuthorIfNotExistsFor = function (authorMapper, name, callback) { mapAuthorWithDBKey("mapper2author", authorMapper, function(err, author) { - //error? - if(err) - { - callback(err); - return; - } + if(ERR(err, callback)) return; //set the name of this author if(name) @@ -84,24 +82,14 @@ function mapAuthorWithDBKey (mapperkey, mapper, callback) //try to map to an author db.get(mapperkey + ":" + mapper, function (err, author) { - //error? - if(err) - { - callback(err); - return; - } + if(ERR(err, callback)) return; //there is no author with this mapper, so create one if(author == null) { exports.createAuthor(null, function(err, author) { - //error? - if(err) - { - callback(err); - return; - } + if(ERR(err, callback)) return; //create the token2author relation db.set(mapperkey + ":" + mapper, author.authorID); diff --git a/node/db/GroupManager.js b/node/db/GroupManager.js index 1598e72e1..473ea9b77 100644 --- a/node/db/GroupManager.js +++ b/node/db/GroupManager.js @@ -18,6 +18,8 @@ * limitations under the License. */ +var ERR = require("async-stacktrace"); +var customError = require("../utils/customError"); var db = require("./DB").db; var async = require("async"); var padManager = require("./PadManager"); @@ -34,15 +36,12 @@ exports.deleteGroup = function(groupID, callback) //try to get the group entry db.get("group:" + groupID, function (err, _group) { - //error - if(err) - { - callback(err); - } + if(ERR(err, callback)) return; + //group does not exist - else if(_group == null) + if(_group == null) { - callback({stop: "groupID does not exist"}); + callback(new customError("groupID does not exist","apierror")); } //group exists, everything is fine else @@ -67,7 +66,7 @@ exports.deleteGroup = function(groupID, callback) { padManager.getPad(padID, function(err, pad) { - if(err) {callback(err); return} + if(ERR(err, callback)) return; pad.remove(callback); }); @@ -79,7 +78,7 @@ exports.deleteGroup = function(groupID, callback) //try to get the group entry db.get("group2sessions:" + groupID, function (err, group2sessions) { - if(err) {callback(err); return} + if(ERR(err, callback)) return; //skip if there is no group2sessions entry if(group2sessions == null) {callback(); return} @@ -107,7 +106,8 @@ exports.deleteGroup = function(groupID, callback) } ], function(err) { - callback(err); + if(ERR(err, callback)) return; + callback(); }); } @@ -116,7 +116,8 @@ exports.doesGroupExist = function(groupID, callback) //try to get the group entry db.get("group:" + groupID, function (err, group) { - callback(err, group != null); + if(ERR(err, callback)) return; + callback(null, group != null); }); } @@ -135,30 +136,21 @@ exports.createGroupIfNotExistsFor = function(groupMapper, callback) //ensure mapper is optional if(typeof groupMapper != "string") { - callback({stop: "groupMapper is no string"}); + callback(new customError("groupMapper is no string","apierror")); return; } //try to get a group for this mapper db.get("mapper2group:"+groupMapper, function(err, groupID) { - if(err) - { - callback(err); - return; - } + if(ERR(err, callback)) return; //there is no group for this mapper, let's create a group if(groupID == null) { exports.createGroup(function(err, responseObj) { - //check for errors - if(err) - { - callback(err); - return; - } + if(ERR(err, callback)) return; //create the mapper entry for this group db.set("mapper2group:"+groupMapper, responseObj.groupID); @@ -169,7 +161,8 @@ exports.createGroupIfNotExistsFor = function(groupMapper, callback) //there is a group for this mapper, let's return it else { - callback(err, {groupID: groupID}); + if(ERR(err, callback)) return; + callback(null, {groupID: groupID}); } }); } @@ -185,15 +178,12 @@ exports.createGroupPad = function(groupID, padName, text, callback) { exports.doesGroupExist(groupID, function(err, exists) { - //error - if(err) - { - callback(err); - } + if(ERR(err, callback)) return; + //group does not exist - else if(exists == false) + if(exists == false) { - callback({stop: "groupID does not exist"}); + callback(new customError("groupID does not exist","apierror")); } //group exists, everything is fine else @@ -207,15 +197,12 @@ exports.createGroupPad = function(groupID, padName, text, callback) { padManager.doesPadExists(padID, function(err, exists) { - //error - if(err) - { - callback(err); - } + if(ERR(err, callback)) return; + //pad exists already - else if(exists == true) + if(exists == true) { - callback({stop: "padName does already exist"}); + callback(new customError("padName does already exist","apierror")); } //pad does not exist, everything is fine else @@ -229,7 +216,8 @@ exports.createGroupPad = function(groupID, padName, text, callback) { padManager.getPad(padID, text, function(err) { - callback(err); + if(ERR(err, callback)) return; + callback(); }); }, //create an entry in the group for this pad @@ -240,7 +228,8 @@ exports.createGroupPad = function(groupID, padName, text, callback) } ], function(err) { - callback(err, {padID: padID}); + if(ERR(err, callback)) return; + callback(null, {padID: padID}); }); } @@ -248,22 +237,20 @@ exports.listPads = function(groupID, callback) { exports.doesGroupExist(groupID, function(err, exists) { - //error - if(err) - { - callback(err); - } + if(ERR(err, callback)) return; + //group does not exist - else if(exists == false) + if(exists == false) { - callback({stop: "groupID does not exist"}); + callback(new customError("groupID does not exist","apierror")); } //group exists, let's get the pads else { db.getSub("group:" + groupID, ["pads"], function(err, pads) { - callback(err, {padIDs: pads}); + if(ERR(err, callback)) return; + callback(null, {padIDs: pads}); }); } }); diff --git a/node/db/Pad.js b/node/db/Pad.js index bba7562c8..7807d4643 100644 --- a/node/db/Pad.js +++ b/node/db/Pad.js @@ -4,6 +4,7 @@ require('joose'); +var ERR = require("async-stacktrace"); var Changeset = require("../utils/Changeset"); var AttributePoolFactory = require("../utils/AttributePoolFactory"); var db = require("./DB").db; @@ -164,8 +165,9 @@ Class('Pad', { { db.getSub("pad:"+_this.id+":revs:"+keyRev, ["meta", "atext"], function(err, _atext) { + if(ERR(err, callback)) return; atext = Changeset.cloneAText(_atext); - callback(err); + callback(); }); }, //get all needed changesets @@ -175,8 +177,9 @@ Class('Pad', { { _this.getRevisionChangeset(item, function(err, changeset) { + if(ERR(err, callback)) return; changesets[item] = changeset; - callback(err); + callback(); }); }, callback); } @@ -199,7 +202,8 @@ Class('Pad', { } ], function(err) { - callback(err, atext); + if(ERR(err, callback)) return; + callback(null, atext); }); }, @@ -247,8 +251,9 @@ Class('Pad', { { db.get("pad:"+_this.id+":chat:"+entryNum, function(err, _entry) { + if(ERR(err, callback)) return; entry = _entry; - callback(err); + callback(); }); }, //add the authorName @@ -264,13 +269,15 @@ Class('Pad', { //get the authorName authorManager.getAuthorName(entry.userId, function(err, authorName) { + if(ERR(err, callback)) return; entry.userName = authorName; - callback(err); + callback(); }); } ], function(err) { - callback(err, entry); + if(ERR(err, callback)) return; + callback(null, entry); }); }, @@ -311,11 +318,14 @@ Class('Pad', { { _this.getChatMessage(entryObject.entryNum, function(err, entry) { + if(ERR(err, callback)) return; entries[entryObject.order] = entry; - callback(err); + callback(); }); }, function(err) { + if(ERR(err, callback)) return; + //sort out broken chat entries //it looks like in happend in the past that the chat head was //incremented, but the chat message wasn't added @@ -328,7 +338,7 @@ Class('Pad', { console.warn("WARNING: Found broken chat entry in pad " + _this.id); } - callback(err, cleanedEntries); + callback(null, cleanedEntries); }); }, @@ -345,11 +355,7 @@ Class('Pad', { //try to load the pad db.get("pad:"+this.id, function(err, value) { - if(err) - { - callback(err, null); - return; - } + if(ERR(err, callback)) return; //if this pad exists, load it if(value != null) @@ -410,7 +416,7 @@ Class('Pad', { db.get("group:" + groupID, function (err, group) { - if(err) {callback(err); return} + if(ERR(err, callback)) return; //remove the pad entry delete group.pads[padID]; @@ -432,7 +438,7 @@ Class('Pad', { { readOnlyManager.getReadOnlyId(padID, function(err, readonlyID) { - if(err) {callback(err); return} + if(ERR(err, callback)) return; db.remove("pad2readonly:" + padID); db.remove("readonly2pad:" + readonlyID); @@ -475,7 +481,8 @@ Class('Pad', { } ], function(err) { - callback(err); + if(ERR(err, callback)) return; + callback(); }) }, //set in db diff --git a/node/db/PadManager.js b/node/db/PadManager.js index 4e16c7c45..1aadcd1ff 100644 --- a/node/db/PadManager.js +++ b/node/db/PadManager.js @@ -18,6 +18,8 @@ * limitations under the License. */ +var ERR = require("async-stacktrace"); +var customError = require("../utils/customError"); require("../db/Pad"); var db = require("./DB").db; @@ -30,12 +32,20 @@ var db = require("./DB").db; * If this is needed in other places, it would be wise to make this a prototype * that's defined somewhere more sensible. */ -globalPads = { +var globalPads = { get: function (name) { return this[':'+name]; }, set: function (name, value) { this[':'+name] = value; }, remove: function (name) { delete this[':'+name]; } }; +/** + * An array of padId transformations. These represent changes in pad name policy over + * time, and allow us to "play back" these changes so legacy padIds can be found. + */ +var padIdTransforms = [ + [/\s+/g, '_'] +]; + /** * Returns a Pad Object with the callback * @param id A String with the id of the pad @@ -46,7 +56,7 @@ exports.getPad = function(id, text, callback) //check if this is a valid padId if(!exports.isValidPadId(id)) { - callback({stop: id + " is not a valid padId"}); + callback(new customError(id + " is not a valid padId","apierror")); return; } @@ -63,14 +73,14 @@ exports.getPad = function(id, text, callback) //check if text is a string if(typeof text != "string") { - callback({stop: "text is not a string"}); + callback(new customError("text is not a string","apierror")); return; } //check if text is less than 100k chars if(text.length > 100000) { - callback({stop: "text must be less than 100k chars"}); + callback(new customError("text must be less than 100k chars","apierror")); return; } } @@ -90,15 +100,10 @@ exports.getPad = function(id, text, callback) //initalize the pad pad.init(text, function(err) { - if(err) - { - callback(err, null); - } - else - { - globalPads.set(id, pad); - callback(null, pad); - } + if(ERR(err, callback)) return; + + globalPads.set(id, pad); + callback(null, pad); }); } } @@ -108,10 +113,44 @@ exports.doesPadExists = function(padId, callback) { db.get("pad:"+padId, function(err, value) { - callback(err, value != null); + if(ERR(err, callback)) return; + callback(null, value != null); }); } +//returns a sanitized padId, respecting legacy pad id formats +exports.sanitizePadId = function(padId, callback) { + var transform_index = arguments[2] || 0; + //we're out of possible transformations, so just return it + if(transform_index >= padIdTransforms.length) + { + callback(padId); + } + //check if padId exists + else + { + exports.doesPadExists(padId, function(junk, exists) + { + if(exists) + { + callback(padId); + } + else + { + //get the next transformation *that's different* + var transformedPadId = padId; + while(transformedPadId == padId && transform_index < padIdTransforms.length) + { + transformedPadId = padId.replace(padIdTransforms[transform_index][0], padIdTransforms[transform_index][1]); + transform_index += 1; + } + //check the next transform + exports.sanitizePadId(transformedPadId, callback, transform_index); + } + }); + } +} + exports.isValidPadId = function(padId) { return /^(g.[a-zA-Z0-9]{16}\$)?[^$]{1,50}$/.test(padId); diff --git a/node/db/ReadOnlyManager.js b/node/db/ReadOnlyManager.js index cd18a1888..73b3be9ec 100644 --- a/node/db/ReadOnlyManager.js +++ b/node/db/ReadOnlyManager.js @@ -18,6 +18,7 @@ * limitations under the License. */ +var ERR = require("async-stacktrace"); var db = require("./DB").db; var async = require("async"); @@ -55,8 +56,9 @@ exports.getReadOnlyId = function (padId, callback) } ], function(err) { + if(ERR(err, callback)) return; //return the results - callback(err, readOnlyId); + callback(null, readOnlyId); }) } diff --git a/node/db/SecurityManager.js b/node/db/SecurityManager.js index 0f35d8735..52d5afcbe 100644 --- a/node/db/SecurityManager.js +++ b/node/db/SecurityManager.js @@ -17,7 +17,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - + +var ERR = require("async-stacktrace"); var db = require("./DB").db; var async = require("async"); var authorManager = require("./AuthorManager"); @@ -56,6 +57,8 @@ exports.checkAccess = function (padID, sessionID, token, password, callback) //get author for this token authorManager.getAuthor4Token(token, function(err, author) { + if(ERR(err, callback)) return; + // assume user has access statusObject = {accessStatus: "grant", authorID: author}; // user can't create pads @@ -64,17 +67,19 @@ exports.checkAccess = function (padID, sessionID, token, password, callback) // check if pad exists padManager.doesPadExists(padID, function(err, exists) { + if(ERR(err, callback)) return; + // pad doesn't exist - user can't have access if(!exists) statusObject.accessStatus = "deny"; // grant or deny access, with author of token - callback(err, statusObject); + callback(null, statusObject); }); } // user may create new pads - no need to check anything else { // grant access, with author of token - callback(err, statusObject); + callback(null, statusObject); } }) @@ -102,8 +107,9 @@ exports.checkAccess = function (padID, sessionID, token, password, callback) { padManager.doesPadExists(padID, function(err, exists) { + if(ERR(err, callback)) return; padExists = exists; - callback(err); + callback(); }); }, //get informations about this session @@ -112,13 +118,13 @@ exports.checkAccess = function (padID, sessionID, token, password, callback) sessionManager.getSessionInfo(sessionID, function(err, sessionInfo) { //skip session validation if the session doesn't exists - if(err && err.stop == "sessionID does not exist") + if(err && err.message == "sessionID does not exist") { callback(); return; } - if(err) {callback(err); return} + if(ERR(err, callback)) return; var now = Math.floor(new Date().getTime()/1000); @@ -139,8 +145,9 @@ exports.checkAccess = function (padID, sessionID, token, password, callback) //get author for this token authorManager.getAuthor4Token(token, function(err, author) { + if(ERR(err, callback)) return; tokenAuthor = author; - callback(err); + callback(); }); } ], callback); @@ -157,7 +164,7 @@ exports.checkAccess = function (padID, sessionID, token, password, callback) padManager.getPad(padID, function(err, pad) { - if(err) {callback(err); return} + if(ERR(err, callback)) return; //is it a public pad? isPublic = pad.getPublicStatus(); @@ -265,6 +272,7 @@ exports.checkAccess = function (padID, sessionID, token, password, callback) } ], function(err) { - callback(err, statusObject); + if(ERR(err, callback)) return; + callback(null, statusObject); }); } diff --git a/node/db/SessionManager.js b/node/db/SessionManager.js index fc6fe306d..a394f5442 100644 --- a/node/db/SessionManager.js +++ b/node/db/SessionManager.js @@ -18,6 +18,8 @@ * limitations under the License. */ +var ERR = require("async-stacktrace"); +var customError = require("../utils/customError"); var db = require("./DB").db; var async = require("async"); var groupMangager = require("./GroupManager"); @@ -28,7 +30,8 @@ exports.doesSessionExist = function(sessionID, callback) //check if the database entry of this session exists db.get("session:" + sessionID, function (err, session) { - callback(err, session != null); + if(ERR(err, callback)) return; + callback(null, session != null); }); } @@ -45,15 +48,12 @@ exports.createSession = function(groupID, authorID, validUntil, callback) { groupMangager.doesGroupExist(groupID, function(err, exists) { - //error - if(err) - { - callback(err); - } + if(ERR(err, callback)) return; + //group does not exist - else if(exists == false) + if(exists == false) { - callback({stop: "groupID does not exist"}); + callback(new customError("groupID does not exist","apierror")); } //everything is fine, continue else @@ -67,15 +67,12 @@ exports.createSession = function(groupID, authorID, validUntil, callback) { authorMangager.doesAuthorExists(authorID, function(err, exists) { - //error - if(err) - { - callback(err); - } + if(ERR(err, callback)) return; + //author does not exist - else if(exists == false) + if(exists == false) { - callback({stop: "authorID does not exist"}); + callback(new customError("authorID does not exist","apierror")); } //everything is fine, continue else @@ -97,7 +94,7 @@ exports.createSession = function(groupID, authorID, validUntil, callback) } else { - callback({stop: "validUntil is not a number"}); + callback(new customError("validUntil is not a number","apierror")); return; } } @@ -105,21 +102,21 @@ exports.createSession = function(groupID, authorID, validUntil, callback) //ensure this is not a negativ number if(validUntil < 0) { - callback({stop: "validUntil is a negativ number"}); + callback(new customError("validUntil is a negativ number","apierror")); return; } //ensure this is not a float value if(!is_int(validUntil)) { - callback({stop: "validUntil is a float value"}); + callback(new customError("validUntil is a float value","apierror")); return; } //check if validUntil is in the future if(Math.floor(new Date().getTime()/1000) > validUntil) { - callback({stop: "validUntil is in the past"}); + callback(new customError("validUntil is in the past","apierror")); return; } @@ -137,12 +134,7 @@ exports.createSession = function(groupID, authorID, validUntil, callback) //get the entry db.get("group2sessions:" + groupID, function(err, group2sessions) { - //did a error happen? - if(err) - { - callback(err); - return; - } + if(ERR(err, callback)) return; //the entry doesn't exist so far, let's create it if(group2sessions == null) @@ -165,12 +157,7 @@ exports.createSession = function(groupID, authorID, validUntil, callback) //get the entry db.get("author2sessions:" + authorID, function(err, author2sessions) { - //did a error happen? - if(err) - { - callback(err); - return; - } + if(ERR(err, callback)) return; //the entry doesn't exist so far, let's create it if(author2sessions == null) @@ -189,8 +176,10 @@ exports.createSession = function(groupID, authorID, validUntil, callback) } ], function(err) { + if(ERR(err, callback)) return; + //return error and sessionID - callback(err, {sessionID: sessionID}); + callback(null, {sessionID: sessionID}); }) } @@ -199,15 +188,12 @@ exports.getSessionInfo = function(sessionID, callback) //check if the database entry of this session exists db.get("session:" + sessionID, function (err, session) { - //error - if(err) - { - callback(err); - } + if(ERR(err, callback)) return; + //session does not exists - else if(session == null) + if(session == null) { - callback({stop: "sessionID does not exist"}) + callback(new customError("sessionID does not exist","apierror")) } //everything is fine, return the sessioninfos else @@ -231,15 +217,12 @@ exports.deleteSession = function(sessionID, callback) //get the session entry db.get("session:" + sessionID, function (err, session) { - //error - if(err) - { - callback(err); - } + if(ERR(err, callback)) return; + //session does not exists - else if(session == null) + if(session == null) { - callback({stop: "sessionID does not exist"}) + callback(new customError("sessionID does not exist","apierror")) } //everything is fine, return the sessioninfos else @@ -256,8 +239,9 @@ exports.deleteSession = function(sessionID, callback) { db.get("group2sessions:" + groupID, function (err, _group2sessions) { + if(ERR(err, callback)) return; group2sessions = _group2sessions; - callback(err); + callback(); }); }, //get the author2sessions entry @@ -265,8 +249,9 @@ exports.deleteSession = function(sessionID, callback) { db.get("author2sessions:" + authorID, function (err, _author2sessions) { + if(ERR(err, callback)) return; author2sessions = _author2sessions; - callback(err); + callback(); }); }, //remove the values from the database @@ -287,7 +272,8 @@ exports.deleteSession = function(sessionID, callback) } ], function(err) { - callback(err); + if(ERR(err, callback)) return; + callback(); }) } @@ -295,15 +281,12 @@ exports.listSessionsOfGroup = function(groupID, callback) { groupMangager.doesGroupExist(groupID, function(err, exists) { - //error - if(err) - { - callback(err); - } + if(ERR(err, callback)) return; + //group does not exist - else if(exists == false) + if(exists == false) { - callback({stop: "groupID does not exist"}); + callback(new customError("groupID does not exist","apierror")); } //everything is fine, continue else @@ -317,15 +300,12 @@ exports.listSessionsOfAuthor = function(authorID, callback) { authorMangager.doesAuthorExists(authorID, function(err, exists) { - //error - if(err) - { - callback(err); - } + if(ERR(err, callback)) return; + //group does not exist - else if(exists == false) + if(exists == false) { - callback({stop: "authorID does not exist"}); + callback(new customError("authorID does not exist","apierror")); } //everything is fine, continue else @@ -346,8 +326,9 @@ function listSessionsWithDBKey (dbkey, callback) //get the group2sessions entry db.get(dbkey, function(err, sessionObject) { + if(ERR(err, callback)) return; sessions = sessionObject ? sessionObject.sessionIDs : null; - callback(err); + callback(); }); }, function(callback) @@ -364,14 +345,16 @@ function listSessionsWithDBKey (dbkey, callback) { exports.getSessionInfo(sessionID, function(err, sessionInfo) { + if(ERR(err, callback)) return; sessions[sessionID] = sessionInfo; - callback(err); + callback(); }); }, callback); } ], function(err) { - callback(err, sessions); + if(ERR(err, callback)) return; + callback(null, sessions); }); } diff --git a/node/easysync_tests.js b/node/easysync_tests.js index e0d82c33f..5b73b7170 100644 --- a/node/easysync_tests.js +++ b/node/easysync_tests.js @@ -20,8 +20,8 @@ * limitations under the License. */ -var Changeset = require('./Changeset'); -var AttributePoolFactory = require("./AttributePoolFactory"); +var Changeset = require('./utils/Changeset'); +var AttributePoolFactory = require("./utils/AttributePoolFactory"); function random() { this.nextInt = function (maxValue) { diff --git a/node/handler/APIHandler.js b/node/handler/APIHandler.js index 2159cc406..0cd9cb58e 100644 --- a/node/handler/APIHandler.js +++ b/node/handler/APIHandler.js @@ -18,8 +18,10 @@ * limitations under the License. */ +var ERR = require("async-stacktrace"); var fs = require("fs"); var api = require("../db/API"); +var padManager = require("../db/PadManager"); //ensure we have an apikey var apikey = null; @@ -94,7 +96,33 @@ exports.handle = function(functionName, fields, req, res) res.send({code: 3, message: "no such function", data: null}); return; } - + + //sanitize any pad id's before continuing + if(fields["padID"]) + { + padManager.sanitizePadId(fields["padID"], function(padId) + { + fields["padID"] = padId; + callAPI(functionName, fields, req, res); + }); + } + else if(fields["padName"]) + { + padManager.sanitizePadId(fields["padName"], function(padId) + { + fields["padName"] = padId; + callAPI(functionName, fields, req, res); + }); + } + else + { + callAPI(functionName, fields, req, res); + } +} + +//calls the api function +function callAPI(functionName, fields, req, res) +{ //put the function parameters in an array var functionParams = []; for(var i=0;i -1) - { - setTimeout(function() - { - fs.unlink(destFile, callback); - }, 100); - } + if(os.type().indexOf("Windows") > -1) + { + setTimeout(function() + { + fs.unlink(destFile, callback); + }, 100); + } + else + { + fs.unlink(destFile, callback); + } } ], callback); } ], function(err) { - if(err && err != "stop") throw err; + if(err && err != "stop") ERR(err); }) } }; diff --git a/node/handler/ImportHandler.js b/node/handler/ImportHandler.js index 283d105dd..06a2ce5ca 100644 --- a/node/handler/ImportHandler.js +++ b/node/handler/ImportHandler.js @@ -18,6 +18,7 @@ * limitations under the License. */ +var ERR = require("async-stacktrace"); var padManager = require("../db/PadManager"); var padMessageHandler = require("./PadMessageHandler"); var async = require("async"); @@ -122,8 +123,9 @@ exports.doImport = function(req, res, padId) { padManager.getPad(padId, function(err, _pad) { + if(ERR(err, callback)) return; pad = _pad; - callback(err); + callback(); }); }, @@ -132,17 +134,22 @@ exports.doImport = function(req, res, padId) { fs.readFile(destFile, "utf8", function(err, _text) { + if(ERR(err, callback)) return; text = _text; //node on windows has a delay on releasing of the file lock. //We add a 100ms delay to work around this - if(os.type().indexOf("Windows") > -1) - { + if(os.type().indexOf("Windows") > -1) + { setTimeout(function() { - callback(err); + callback(); }, 100); - } + } + else + { + callback(); + } }); }, @@ -176,7 +183,7 @@ exports.doImport = function(req, res, padId) return; } - if(err) throw err; + ERR(err); //close the connection res.send("ok"); diff --git a/node/handler/PadMessageHandler.js b/node/handler/PadMessageHandler.js index 188da5aea..0b48320b3 100644 --- a/node/handler/PadMessageHandler.js +++ b/node/handler/PadMessageHandler.js @@ -18,6 +18,7 @@ * limitations under the License. */ +var ERR = require("async-stacktrace"); var async = require("async"); var padManager = require("../db/PadManager"); var Changeset = require("../utils/Changeset"); @@ -27,7 +28,6 @@ var readOnlyManager = require("../db/ReadOnlyManager"); var settings = require('../utils/Settings'); var securityManager = require("../db/SecurityManager"); var log4js = require('log4js'); -var os = require("os"); var messageLogger = log4js.getLogger("message"); /** @@ -107,7 +107,7 @@ exports.handleDisconnect = function(client) //get the author color out of the db authorManager.getAuthorColorId(author, function(err, color) { - if(err) throw err; + ERR(err); //prepare the notification for the other users on the pad, that this user left var messageToTheOtherUsers = { @@ -218,16 +218,18 @@ function handleChatMessage(client, message) { padManager.getPad(padId, function(err, _pad) { + if(ERR(err, callback)) return; pad = _pad; - callback(err); + callback(); }); }, function(callback) { authorManager.getAuthorName(userId, function(err, _userName) { + if(ERR(err, callback)) return; userName = _userName; - callback(err); + callback(); }); }, //save the chat message and broadcast it @@ -257,7 +259,7 @@ function handleChatMessage(client, message) } ], function(err) { - if(err) throw err; + ERR(err); }); } @@ -375,8 +377,9 @@ function handleUserChanges(client, message) { padManager.getPad(session2pad[client.id], function(err, value) { + if(ERR(err, callback)) return; pad = value; - callback(err); + callback(); }); }, //create the changeset @@ -422,16 +425,10 @@ function handleUserChanges(client, message) pad.getRevisionChangeset(r, function(err, c) { - if(err) - { - callback(err); - return; - } - else - { - changeset = Changeset.follow(c, changeset, false, apool); - callback(null); - } + if(ERR(err, callback)) return; + + changeset = Changeset.follow(c, changeset, false, apool); + callback(null); }); }, //use the callback of the series function @@ -469,7 +466,7 @@ function handleUserChanges(client, message) } ], function(err) { - if(err) throw err; + ERR(err); }); } @@ -502,25 +499,23 @@ exports.updatePadClients = function(pad, callback) { pad.getRevisionAuthor(r, function(err, value) { + if(ERR(err, callback)) return; author = value; - callback(err); + callback(); }); }, function (callback) { pad.getRevisionChangeset(r, function(err, value) { + if(ERR(err, callback)) return; revChangeset = value; - callback(err); + callback(); }); } ], function(err) { - if(err) - { - callback(err); - return; - } + if(ERR(err, callback)) return; if(author == sessioninfos[session].author) { @@ -633,7 +628,7 @@ function handleClientReady(client, message) { securityManager.checkAccess (message.padId, message.sessionID, message.token, message.password, function(err, statusObject) { - if(err) {callback(err); return} + if(ERR(err, callback)) return; //access was granted if(statusObject.accessStatus == "grant") @@ -657,8 +652,9 @@ function handleClientReady(client, message) { authorManager.getAuthorColorId(author, function(err, value) { + if(ERR(err, callback)) return; authorColorId = value; - callback(err); + callback(); }); }, //get author name @@ -666,24 +662,27 @@ function handleClientReady(client, message) { authorManager.getAuthorName(author, function(err, value) { + if(ERR(err, callback)) return; authorName = value; - callback(err); + callback(); }); }, function(callback) { padManager.getPad(message.padId, function(err, value) { + if(ERR(err, callback)) return; pad = value; - callback(err); + callback(); }); }, function(callback) { readOnlyManager.getReadOnlyId(message.padId, function(err, value) { + if(ERR(err, callback)) return; readOnlyId = value; - callback(err); + callback(); }); } ], callback); @@ -701,9 +700,10 @@ function handleClientReady(client, message) { authorManager.getAuthor(authorId, function(err, author) { + if(ERR(err, callback)) return; delete author.timestamp; historicalAuthorData[authorId] = author; - callback(err); + callback(); }); }, callback); }, @@ -712,8 +712,9 @@ function handleClientReady(client, message) { pad.getLastChatMessages(100, function(err, _chatMessages) { + if(ERR(err, callback)) return; chatMessages = _chatMessages; - callback(err); + callback(); }); } ], callback); @@ -753,13 +754,6 @@ function handleClientReady(client, message) var apool = attribsForWire.pool.toJsonable(); atext.attribs = attribsForWire.translated; - //check if abiword is avaiable - var abiwordAvailable = settings.abiword != null ? "yes" : "no"; - if(settings.abiword != null && os.type().indexOf("Windows") != -1) - { - abiwordAvailable = "withoutPDF"; - } - var clientVars = { "accountPrivs": { "maxRevisions": 100 @@ -796,7 +790,7 @@ function handleClientReady(client, message) "fullWidth": false, "hideSidebar": false }, - "abiwordAvailable": abiwordAvailable, + "abiwordAvailable": settings.abiwordAvailable(), "hooks": {} } @@ -806,24 +800,26 @@ function handleClientReady(client, message) clientVars.userName = authorName; } - //This is a reconnect, so we don't have to send the client the ClientVars again - if(message.reconnect == true) + if(sessioninfos[client.id] !== undefined) { - //Save the revision in sessioninfos, we take the revision from the info the client send to us - sessioninfos[client.id].rev = message.client_rev; + //This is a reconnect, so we don't have to send the client the ClientVars again + if(message.reconnect == true) + { + //Save the revision in sessioninfos, we take the revision from the info the client send to us + sessioninfos[client.id].rev = message.client_rev; + } + //This is a normal first connect + else + { + //Send the clientVars to the Client + client.json.send(clientVars); + //Save the revision in sessioninfos + sessioninfos[client.id].rev = pad.getHeadRevisionNumber(); + } + + //Save the revision and the author id in sessioninfos + sessioninfos[client.id].author = author; } - //This is a normal first connect - else - { - //Send the clientVars to the Client - client.json.send(clientVars); - //Save the revision in sessioninfos - sessioninfos[client.id].rev = pad.getHeadRevisionNumber(); - } - - //Save the revision and the author id in sessioninfos - sessioninfos[client.id].rev = pad.getHeadRevisionNumber(); - sessioninfos[client.id].author = author; //prepare the notification for the other users on the pad, that this user joined var messageToTheOtherUsers = { @@ -859,16 +855,18 @@ function handleClientReady(client, message) { authorManager.getAuthorColorId(sessioninfos[sessionID].author, function(err, value) { + if(ERR(err, callback)) return; sessionAuthorColorId = value; - callback(err); + callback(); }) }, function(callback) { authorManager.getAuthorName(sessioninfos[sessionID].author, function(err, value) { + if(ERR(err, callback)) return; sessionAuthorName = value; - callback(err); + callback(); }) } ],callback); @@ -903,6 +901,6 @@ function handleClientReady(client, message) } ],function(err) { - if(err) throw err; + ERR(err); }); } diff --git a/node/handler/SocketIORouter.js b/node/handler/SocketIORouter.js index 00e3e4f3f..f3b82b8c7 100644 --- a/node/handler/SocketIORouter.js +++ b/node/handler/SocketIORouter.js @@ -19,6 +19,7 @@ * limitations under the License. */ +var ERR = require("async-stacktrace"); var log4js = require('log4js'); var messageLogger = log4js.getLogger("message"); var securityManager = require("../db/SecurityManager"); @@ -109,7 +110,7 @@ exports.setSocketIO = function(_socket) { securityManager.checkAccess (message.padId, message.sessionID, message.token, message.password, function(err, statusObject) { - if(err) throw err; + ERR(err); //access was granted, mark the client as authorized and handle the message if(statusObject.accessStatus == "grant") diff --git a/node/handler/TimesliderMessageHandler.js b/node/handler/TimesliderMessageHandler.js index 86864977f..b30a9fc9d 100644 --- a/node/handler/TimesliderMessageHandler.js +++ b/node/handler/TimesliderMessageHandler.js @@ -18,10 +18,12 @@ * limitations under the License. */ +var ERR = require("async-stacktrace"); var async = require("async"); var padManager = require("../db/PadManager"); var Changeset = require("../utils/Changeset"); var AttributePoolFactory = require("../utils/AttributePoolFactory"); +var settings = require('../utils/Settings'); var authorManager = require("../db/AuthorManager"); var log4js = require('log4js'); var messageLogger = log4js.getLogger("message"); @@ -92,7 +94,7 @@ function handleClientReady(client, message) //send the timeslider client the clientVars, with this values its able to start createTimesliderClientVars (message.padId, function(err, clientVars) { - if(err) throw err; + ERR(err); client.json.send({type: "CLIENT_VARS", data: clientVars}); }) @@ -138,7 +140,7 @@ function handleChangesetRequest(client, message) //build the requested rough changesets and send them back getChangesetInfo(padId, start, end, granularity, function(err, changesetInfo) { - if(err) throw err; + ERR(err); var data = changesetInfo; data.requestID = message.data.requestID; @@ -159,6 +161,7 @@ function createTimesliderClientVars (padId, callback) fullWidth: false, disableRightBar: false, initialChangesets: [], + abiwordAvailable: settings.abiwordAvailable(), hooks: [], initialStyledContents: {} }; @@ -171,8 +174,9 @@ function createTimesliderClientVars (padId, callback) { padManager.getPad(padId, function(err, _pad) { + if(ERR(err, callback)) return; pad = _pad; - callback(err); + callback(); }); }, //get all authors and add them to @@ -187,15 +191,17 @@ function createTimesliderClientVars (padId, callback) { authorManager.getAuthor(authorId, function(err, author) { + if(ERR(err, callback)) return; historicalAuthorData[authorId] = author; - callback(err); + callback(); }); }, function(err) { + if(ERR(err, callback)) return; //add historicalAuthorData to the clientVars and continue clientVars.historicalAuthorData = historicalAuthorData; clientVars.initialStyledContents.historicalAuthorData = historicalAuthorData; - callback(err); + callback(); }); }, //get the timestamp of the last revision @@ -203,8 +209,9 @@ function createTimesliderClientVars (padId, callback) { pad.getRevisionDate(pad.getHeadRevisionNumber(), function(err, date) { + if(ERR(err, callback)) return; clientVars.currentTime = date; - callback(err); + callback(); }); }, function(callback) @@ -235,14 +242,16 @@ function createTimesliderClientVars (padId, callback) Math.floor(lastRev / topGranularity)*topGranularity+topGranularity, granularity, function(err, changeset) { + if(ERR(err, callback)) return; clientVars.initialChangesets.push(changeset); - callback(err); + callback(); }); }, callback); } ], function(err) { - callback(err, clientVars); + if(ERR(err, callback)) return; + callback(null, clientVars); }); } @@ -267,8 +276,9 @@ function getChangesetInfo(padId, startNum, endNum, granularity, callback) { padManager.getPad(padId, function(err, _pad) { + if(ERR(err, callback)) return; pad = _pad; - callback(err); + callback(); }); }, function(callback) @@ -309,8 +319,9 @@ function getChangesetInfo(padId, startNum, endNum, granularity, callback) { composePadChangesets(padId, item.start, item.end, function(err, changeset) { + if(ERR(err, callback)) return; composedChangesets[item.start + "/" + item.end] = changeset; - callback(err); + callback(); }); }, callback); }, @@ -321,8 +332,9 @@ function getChangesetInfo(padId, startNum, endNum, granularity, callback) { pad.getRevisionDate(revNum, function(err, revDate) { + if(ERR(err, callback)) return; revisionDate[revNum] = Math.floor(revDate/1000); - callback(err); + callback(); }); }, callback); }, @@ -331,8 +343,9 @@ function getChangesetInfo(padId, startNum, endNum, granularity, callback) { getPadLines(padId, startNum-1, function(err, _lines) { + if(ERR(err, callback)) return; lines = _lines; - callback(err); + callback(); }); } ], callback); @@ -383,20 +396,15 @@ function getChangesetInfo(padId, startNum, endNum, granularity, callback) } ], function(err) { - if(err) - { - callback(err); - } - else - { - callback(null, {forwardsChangesets: forwardsChangesets, - backwardsChangesets: backwardsChangesets, - apool: apool.toJsonable(), - actualEndNum: endNum, - timeDeltas: timeDeltas, - start: startNum, - granularity: granularity }); - } + if(ERR(err, callback)) return; + + callback(null, {forwardsChangesets: forwardsChangesets, + backwardsChangesets: backwardsChangesets, + apool: apool.toJsonable(), + actualEndNum: endNum, + timeDeltas: timeDeltas, + start: startNum, + granularity: granularity }); }); } @@ -416,8 +424,9 @@ function getPadLines(padId, revNum, callback) { padManager.getPad(padId, function(err, _pad) { + if(ERR(err, callback)) return; pad = _pad; - callback(err); + callback(); }); }, //get the atext @@ -427,8 +436,9 @@ function getPadLines(padId, revNum, callback) { pad.getInternalRevisionAText(revNum, function(err, _atext) { + if(ERR(err, callback)) return; atext = _atext; - callback(err); + callback(); }); } else @@ -445,7 +455,8 @@ function getPadLines(padId, revNum, callback) } ], function(err) { - callback(err, result); + if(ERR(err, callback)) return; + callback(null, result); }); } @@ -465,8 +476,9 @@ function composePadChangesets(padId, startNum, endNum, callback) { padManager.getPad(padId, function(err, _pad) { + if(ERR(err, callback)) return; pad = _pad; - callback(err); + callback(); }); }, //fetch all changesets we need @@ -486,8 +498,9 @@ function composePadChangesets(padId, startNum, endNum, callback) { pad.getRevisionChangeset(revNum, function(err, value) { + if(ERR(err, callback)) return; changesets[revNum] = value; - callback(err); + callback(); }); },callback); }, @@ -509,7 +522,7 @@ function composePadChangesets(padId, startNum, endNum, callback) //return err and changeset function(err) { - if(err) throw err; - callback(err, changeset); + if(ERR(err, callback)) return; + callback(null, changeset); }); } diff --git a/node/server.js b/node/server.js index 08c09ab0e..6a4f0cfe2 100644 --- a/node/server.js +++ b/node/server.js @@ -20,6 +20,7 @@ * limitations under the License. */ +var ERR = require("async-stacktrace"); var log4js = require('log4js'); var os = require("os"); var socketio = require('socket.io'); @@ -91,6 +92,9 @@ async.waterfall([ 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")) @@ -98,6 +102,12 @@ async.waterfall([ app.use(express.cookieParser()); }); + app.error(function(err, req, res, next){ + res.send(500); + console.error(err.stack ? err.stack : err.toString()); + gracefulShutdown(); + }); + //serve static files app.get('/static/*', function(req, res) { @@ -129,7 +139,7 @@ async.waterfall([ { securityManager.checkAccess(req.params.pad, req.cookies.sessionid, req.cookies.token, req.cookies.password, function(err, accessObj) { - if(err) throw err; + if(ERR(err, callback)) return; //there is access, continue if(accessObj.accessStatus == "grant") @@ -143,6 +153,26 @@ async.waterfall([ } }); } + + //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) @@ -159,12 +189,14 @@ async.waterfall([ { 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(err); + callback(); }); }, //render the html document @@ -182,8 +214,9 @@ async.waterfall([ //render the html document exporthtml.getPadHTMLDocument(padId, null, false, function(err, _html) { + if(ERR(err, callback)) return; html = _html; - callback(err); + callback(); }); }); } @@ -191,7 +224,7 @@ async.waterfall([ { //throw any unexpected error if(err && err != "notfound") - throw err; + ERR(err); if(err == "notfound") res.send('404 - Not Found', 404); @@ -199,100 +232,108 @@ async.waterfall([ res.send(html); }); }); - - //serve pad.html under /p - app.get('/p/:pad', function(req, res, next) - { + + //redirects browser to the pad's sanitized url if needed. otherwise, renders the html + function goToPad(req, res, render) { //ensure the padname is valid and the url doesn't end with a / if(!padManager.isValidPadId(req.params.pad) || /\/$/.test(req.url)) { res.send('Such a padname is forbidden', 404); - return; } - - res.header("Server", serverName); - var filePath = path.normalize(__dirname + "/../static/pad.html"); - res.sendfile(filePath, { maxAge: exports.maxAge }); + else + { + padManager.sanitizePadId(req.params.pad, function(padId) { + //the pad id was sanitized, so we redirect to the sanitized version + if(padId != req.params.pad) + { + var real_path = req.path.replace(/^\/p\/[^\/]+/, '/p/' + padId); + res.header('Location', real_path); + res.send('You should be redirected to ' + real_path + '', 302); + } + //the pad id was fine, so just render it + else + { + render(); + } + }); + } + } + + //serve pad.html under /p + app.get('/p/:pad', function(req, res, next) + { + goToPad(req, res, function() { + res.header("Server", serverName); + 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) { - //ensure the padname is valid and the url doesn't end with a / - if(!padManager.isValidPadId(req.params.pad) || /\/$/.test(req.url)) - { - res.send('Such a padname is forbidden', 404); - return; - } - - res.header("Server", serverName); - var filePath = path.normalize(__dirname + "/../static/timeslider.html"); - res.sendfile(filePath, { maxAge: exports.maxAge }); + goToPad(req, res, function() { + res.header("Server", serverName); + 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/export/:type', function(req, res, next) + app.get('/p/:pad/:rev?/export/:type', function(req, res, next) { - //ensure the padname is valid and the url doesn't end with a / - if(!padManager.isValidPadId(req.params.pad) || /\/$/.test(req.url)) - { - res.send('Such a padname is forbidden', 404); - return; - } - - var types = ["pdf", "doc", "txt", "html", "odt"]; - //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 && req.params.type != "html" && req.params.type != "txt" ) - { - 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", "*"); - res.header("Server", serverName); - - hasPadAccess(req, res, function() - { - exportHandler.doExport(req, res, req.params.pad, req.params.type); + goToPad(req, res, function() { + 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", "*"); + res.header("Server", serverName); + + 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) { - //ensure the padname is valid and the url doesn't end with a / - if(!padManager.isValidPadId(req.params.pad) || /\/$/.test(req.url)) - { - res.send('Such a padname is forbidden', 404); - return; - } - - //if abiword is disabled, skip handling this request - if(settings.abiword == null) - { - next(); - return; - } + goToPad(req, res, function() { + //if abiword is disabled, skip handling this request + if(settings.abiword == null) + { + next(); + return; + } - res.header("Server", serverName); - - hasPadAccess(req, res, function() - { - importHandler.doImport(req, res, req.params.pad); + res.header("Server", serverName); + + 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) { + var apiCaller = function(req, res, fields) + { res.header("Server", serverName); res.header("Content-Type", "application/json; charset=utf-8"); diff --git a/node/utils/Changeset.js b/node/utils/Changeset.js index 2c9e06838..9e1b60ebe 100644 --- a/node/utils/Changeset.js +++ b/node/utils/Changeset.js @@ -3,6 +3,12 @@ * Can be found in https://github.com/ether/pad/blob/master/infrastructure/ace/www/easysync2.js */ +/** + * This code is mostly from the old Etherpad. Please help us to comment this code. + * This helps other people to understand this code better and helps them to improve it. + * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED + */ + /* * Copyright 2009 Google Inc., 2011 Peter 'Pita' Martischka (Primary Technology Ltd) * diff --git a/node/utils/ExportDokuWiki.js b/node/utils/ExportDokuWiki.js new file mode 100644 index 000000000..48e4b2915 --- /dev/null +++ b/node/utils/ExportDokuWiki.js @@ -0,0 +1,344 @@ +/** + * Copyright 2011 Adrian Lang + * + * 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 async = require("async"); +var Changeset = require("./Changeset"); +var padManager = require("../db/PadManager"); + +function getPadDokuWiki(pad, revNum, callback) +{ + var atext = pad.atext; + var dokuwiki; + async.waterfall([ + // fetch revision atext + + + function (callback) + { + if (revNum != undefined) + { + pad.getInternalRevisionAText(revNum, function (err, revisionAtext) + { + atext = revisionAtext; + callback(err); + }); + } + else + { + callback(null); + } + }, + + // convert atext to dokuwiki text + + function (callback) + { + dokuwiki = getDokuWikiFromAtext(pad, atext); + callback(null); + }], + // run final callback + + + function (err) + { + callback(err, dokuwiki); + }); +} + +function getDokuWikiFromAtext(pad, atext) +{ + var apool = pad.apool(); + var textLines = atext.text.slice(0, -1).split('\n'); + var attribLines = Changeset.splitAttributionLines(atext.attribs, atext.text); + + var tags = ['======', '=====', '**', '//', '__', 'del>']; + var props = ['heading1', 'heading2', 'bold', 'italic', 'underline', 'strikethrough']; + var anumMap = {}; + + props.forEach(function (propName, i) + { + var propTrueNum = apool.putAttrib([propName, true], true); + if (propTrueNum >= 0) + { + anumMap[propTrueNum] = i; + } + }); + + function getLineDokuWiki(text, attribs) + { + var propVals = [false, false, false]; + var ENTER = 1; + var STAY = 2; + var LEAVE = 0; + + // Use order of tags (b/i/u) as order of nesting, for simplicity + // and decent nesting. For example, + // Just bold Bold and italics Just italics + // becomes + // Just bold Bold and italics Just italics + var taker = Changeset.stringIterator(text); + var assem = Changeset.stringAssembler(); + + function emitOpenTag(i) + { + if (tags[i].indexOf('>') !== -1) { + assem.append('<'); + } + assem.append(tags[i]); + } + + function emitCloseTag(i) + { + if (tags[i].indexOf('>') !== -1) { + assem.append(' bold, etc. + if (!propVals[i]) + { + propVals[i] = ENTER; + propChanged = true; + } + else + { + propVals[i] = STAY; + } + } + }); + for (var i = 0; i < propVals.length; i++) + { + if (propVals[i] === true) + { + propVals[i] = LEAVE; + propChanged = true; + } + else if (propVals[i] === STAY) + { + propVals[i] = true; // set it back + } + } + // now each member of propVal is in {false,LEAVE,ENTER,true} + // according to what happens at start of span + if (propChanged) + { + // leaving bold (e.g.) also leaves italics, etc. + var left = false; + for (var i = 0; i < propVals.length; i++) + { + var v = propVals[i]; + if (!left) + { + if (v === LEAVE) + { + left = true; + } + } + else + { + if (v === true) + { + propVals[i] = STAY; // tag will be closed and re-opened + } + } + } + + for (var i = propVals.length - 1; i >= 0; i--) + { + if (propVals[i] === LEAVE) + { + emitCloseTag(i); + propVals[i] = false; + } + else if (propVals[i] === STAY) + { + emitCloseTag(i); + } + } + for (var i = 0; i < propVals.length; i++) + { + if (propVals[i] === ENTER || propVals[i] === STAY) + { + emitOpenTag(i); + propVals[i] = true; + } + } + // propVals is now all {true,false} again + } // end if (propChanged) + var chars = o.chars; + if (o.lines) + { + chars--; // exclude newline at end of line, if present + } + var s = taker.take(chars); + + assem.append(_escapeDokuWiki(s)); + } // end iteration over spans in line + for (var i = propVals.length - 1; i >= 0; i--) + { + if (propVals[i]) + { + emitCloseTag(i); + propVals[i] = false; + } + } + } // end processNextChars + if (urls) + { + urls.forEach(function (urlData) + { + var startIndex = urlData[0]; + var url = urlData[1]; + var urlLength = url.length; + processNextChars(startIndex - idx); + assem.append('[['); + + // Do not use processNextChars since a link does not contain syntax and + // needs no escaping + var iter = Changeset.opIterator(Changeset.subattribution(attribs, idx, idx + urlLength)); + idx += urlLength; + assem.append(taker.take(iter.next().chars)); + + assem.append(']]'); + }); + } + processNextChars(text.length - idx); + + return assem.toString() + "\n"; + } // end getLineDokuWiki + var pieces = []; + + for (var i = 0; i < textLines.length; i++) + { + var line = _analyzeLine(textLines[i], attribLines[i], apool); + var lineContent = getLineDokuWiki(line.text, line.aline); + + if (line.listLevel && lineContent) + { + pieces.push(new Array(line.listLevel + 1).join(' ') + '* '); + } + pieces.push(lineContent); + } + + return pieces.join(''); +} + +function _analyzeLine(text, aline, apool) +{ + var line = {}; + + // identify list + var lineMarker = 0; + line.listLevel = 0; + if (aline) + { + var opIter = Changeset.opIterator(aline); + if (opIter.hasNext()) + { + var listType = Changeset.opAttributeValue(opIter.next(), 'list', apool); + if (listType) + { + lineMarker = 1; + listType = /([a-z]+)([12345678])/.exec(listType); + if (listType) + { + line.listTypeName = listType[1]; + line.listLevel = Number(listType[2]); + } + } + } + } + if (lineMarker) + { + line.text = text.substring(1); + line.aline = Changeset.subattribution(aline, 1); + } + else + { + line.text = text; + line.aline = aline; + } + + return line; +} + +exports.getPadDokuWikiDocument = function (padId, revNum, callback) +{ + padManager.getPad(padId, function (err, pad) + { + if (err) + { + callback(err); + return; + } + + getPadDokuWiki(pad, revNum, callback); + }); +} + +function _escapeDokuWiki(s) +{ + s = s.replace(/(\/\/|\*\*|__)/g, '%%$1%%'); + return s; +} + +// copied from ACE +var _REGEX_WORDCHAR = /[\u0030-\u0039\u0041-\u005A\u0061-\u007A\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\u1FFF\u3040-\u9FFF\uF900-\uFDFF\uFE70-\uFEFE\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFDC]/; +var _REGEX_SPACE = /\s/; +var _REGEX_URLCHAR = new RegExp('(' + /[-:@a-zA-Z0-9_.,~%+\/\\?=&#;()$]/.source + '|' + _REGEX_WORDCHAR.source + ')'); +var _REGEX_URL = new RegExp(/(?:(?:https?|s?ftp|ftps|file|smb|afp|nfs|(x-)?man|gopher|txmt):\/\/|mailto:)/.source + _REGEX_URLCHAR.source + '*(?![:.,;])' + _REGEX_URLCHAR.source, 'g'); + +// returns null if no URLs, or [[startIndex1, url1], [startIndex2, url2], ...] + + +function _findURLs(text) +{ + _REGEX_URL.lastIndex = 0; + var urls = null; + var execResult; + while ((execResult = _REGEX_URL.exec(text))) + { + urls = (urls || []); + var startIndex = execResult.index; + var url = execResult[0]; + urls.push([startIndex, url]); + } + + return urls; +} diff --git a/node/utils/ExportHtml.js b/node/utils/ExportHtml.js index fe5ee9e46..8d33d32ef 100644 --- a/node/utils/ExportHtml.js +++ b/node/utils/ExportHtml.js @@ -17,6 +17,7 @@ var async = require("async"); var Changeset = require("./Changeset"); var padManager = require("../db/PadManager"); +var ERR = require("async-stacktrace"); function getPadPlainText(pad, revNum) { @@ -58,8 +59,9 @@ function getPadHTML(pad, revNum, callback) { pad.getInternalRevisionAText(revNum, function (err, revisionAtext) { + if(ERR(err, callback)) return; atext = revisionAtext; - callback(err); + callback(); }); } else @@ -81,7 +83,8 @@ function getPadHTML(pad, revNum, callback) function (err) { - callback(err, html); + if(ERR(err, callback)) return; + callback(null, html); }); } @@ -459,11 +462,7 @@ exports.getPadHTMLDocument = function (padId, revNum, noDocType, callback) { padManager.getPad(padId, function (err, pad) { - if (err) - { - callback(err); - return; - } + if(ERR(err, callback)) return; var head = (noDocType ? '' : '\n') + @@ -488,7 +487,8 @@ exports.getPadHTMLDocument = function (padId, revNum, noDocType, callback) getPadHTML(pad, revNum, function (err, html) { - callback(err, head + html + foot); + if(ERR(err, callback)) return; + callback(null, head + html + foot); }); }); } diff --git a/node/utils/ImportHtml.js b/node/utils/ImportHtml.js index 6441708e1..1b0bcaea7 100644 --- a/node/utils/ImportHtml.js +++ b/node/utils/ImportHtml.js @@ -14,7 +14,7 @@ * limitations under the License. */ -var jsdom = require('jsdom').jsdom; +var jsdom = require('jsdom-nocontextifiy').jsdom; var log4js = require('log4js'); var Changeset = require("./Changeset"); diff --git a/node/utils/Minify.js b/node/utils/Minify.js index dbb409666..588456fd9 100644 --- a/node/utils/Minify.js +++ b/node/utils/Minify.js @@ -19,6 +19,7 @@ * limitations under the License. */ +var ERR = require("async-stacktrace"); var settings = require('./Settings'); var async = require('async'); var fs = require('fs'); @@ -33,7 +34,7 @@ var os = require('os'); var padJS = ["jquery.min.js", "pad_utils.js", "plugins.js", "undo-xpopup.js", "json2.js", "pad_cookie.js", "pad_editor.js", "pad_editbar.js", "pad_docbar.js", "pad_modals.js", "ace.js", "collab_client.js", "pad_userlist.js", "pad_impexp.js", "pad_savedrevs.js", "pad_connectionstatus.js", "pad2.js", "jquery-ui.js", "chat.js", "excanvas.js", "farbtastic.js"]; -var timesliderJS = ["jquery.min.js", "plugins.js", "undo-xpopup.js", "json2.js", "colorutils.js", "draggable.js", "pad_utils.js", "pad_cookie.js", "pad_editor.js", "pad_editbar.js", "pad_docbar.js", "pad_modals.js", "easysync2_client.js", "domline_client.js", "linestylefilter_client.js", "cssmanager_client.js", "broadcast.js", "broadcast_slider.js", "broadcast_revisions.js"]; +var timesliderJS = ["jquery.min.js", "plugins.js", "undo-xpopup.js", "json2.js", "colorutils.js", "draggable.js", "pad_utils.js", "pad_cookie.js", "pad_editor.js", "pad_editbar.js", "pad_docbar.js", "pad_modals.js", "pad_impexp.js", "easysync2_client.js", "domline_client.js", "linestylefilter_client.js", "cssmanager_client.js", "broadcast.js", "broadcast_slider.js", "broadcast_revisions.js"]; /** * creates the minifed javascript for the given minified name @@ -77,7 +78,7 @@ exports.minifyJS = function(req, res, jsFilename) //read the files in the folder fs.readdir(path, function(err, files) { - if(err) { callback(err); return; } + if(ERR(err, callback)) return; //we wanna check the directory itself for changes too files.push("."); @@ -88,7 +89,7 @@ exports.minifyJS = function(req, res, jsFilename) //get the stat data of this file fs.stat(path + "/" + filename, function(err, stats) { - if(err) { callback(err); return; } + if(ERR(err, callback)) return; //get the modification time var modificationTime = stats.mtime.getTime(); @@ -110,7 +111,11 @@ exports.minifyJS = function(req, res, jsFilename) //check the modification time of the minified js fs.stat("../var/minified_" + jsFilename, function(err, stats) { - if(err && err.code != "ENOENT") callback(err); + 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) @@ -131,8 +136,9 @@ exports.minifyJS = function(req, res, jsFilename) { fs.readFile("../static/js/" + item, "utf-8", function(err, data) { + if(ERR(err, callback)) return; fileValues[item] = data; - callback(err); + callback(); }); }, callback); }, @@ -161,6 +167,8 @@ exports.minifyJS = function(req, res, jsFilename) //read the included file fs.readFile(filename, "utf-8", function(err, data) { + if(ERR(err, callback)) return; + //compress the file if(type == "JS") { @@ -188,17 +196,19 @@ exports.minifyJS = function(req, res, jsFilename) embeds[item] = embeds[item].substr(0, embeds[item].length-1); embeds[item] = "'" + embeds[item] + "'"; - callback(err); + callback(); }); }, function(err) { + if(ERR(err, callback)) return; + //replace the include command with the include for(var i in embeds) { fileValues["ace.js"]=fileValues["ace.js"].replace(i, embeds[i]); } - callback(err); + callback(); }); }, //put all together and write it into a file @@ -227,7 +237,10 @@ exports.minifyJS = function(req, res, jsFilename) if(os.type().indexOf("Windows") == -1) { gzip(result, 9, function(err, compressedResult){ - if(err) {callback(err); return} + //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("../var/minified_" + jsFilename + ".gz", compressedResult, callback); }); @@ -242,7 +255,10 @@ exports.minifyJS = function(req, res, jsFilename) } ], function(err) { - if(err && err != "stop") throw 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; @@ -270,15 +286,16 @@ exports.minifyJS = function(req, res, jsFilename) async.forEach(jsFiles, function (item, callback) { fs.readFile("../static/js/" + item, "utf-8", function(err, data) - { + { + if(ERR(err, callback)) return; fileValues[item] = data; - callback(err); + callback(); }); }, //send all files together function(err) { - if(err) throw err; + if(ERR(err)) return; for(var i=0;i li { + padding: 4px 8px; + margin-top: 2px; + } + #chaticon { + opacity: .8; + } + #users { + right: 4px; + } + #mycolorpicker { + left: -72px; /* #mycolorpicker:width - #users:width */ + } + #editorcontainer { + margin-bottom: 33px; + } + #editbar ul#menu_right { + background: #f7f7f7; + background: -moz-linear-gradient(#f7f7f7, #f1f1f1 80%); + background: -ms-linear-gradient(#f7f7f7, #f1f1f1 80%); + background: -o-linear-gradient(#f7f7f7, #f1f1f1 80%); + background: -webkit-linear-gradient(#f7f7f7, #f1f1f1 80%); + width: 100%; + overflow: hidden; + height: 32px; + position: fixed; + bottom: 0; + border-top: 1px solid #ccc; + } + #editbar ul#menu_right li:not(:last-child) { + display: none; + } + #editbar ul#menu_right li:last-child { + height: 24px; + border-radius: 0; + margin-top: 0; + border: 0; + float: right; + } + #chaticon { + bottom: 0; + right: 55px; + border-right: none; + border-radius: 0; + background: #f7f7f7; + background: -moz-linear-gradient(#f7f7f7, #f1f1f1 80%); + background: -ms-linear-gradient(#f7f7f7, #f1f1f1 80%); + background: -o-linear-gradient(#f7f7f7, #f1f1f1 80%); + background: -webkit-linear-gradient(#f7f7f7, #f1f1f1 80%); + border: 0; + } + #chatbox { + bottom: 32px; + right: 0; + border-top-right-radius: 0; + } + #editbar ul li a span { + top: -3px; + } + #usericonback { + margin-top: 4px; + } +} +.rtl{ + direction:RTL; +} diff --git a/static/css/timeslider.css b/static/css/timeslider.css index c808a65c3..9df408683 100644 --- a/static/css/timeslider.css +++ b/static/css/timeslider.css @@ -195,6 +195,11 @@ float:right; color: #222; } +#importexport{ + top:103px; + width:185px; +} + ul { margin-left: 1.5em; } ul ul { margin-left: 0 !important; } ul.list-bullet1 { margin-left: 1.5em; } diff --git a/static/img/fileicons.gif b/static/img/fileicons.gif index 26f63882b64383f535328b22301dd1a12866a425..c03b6031a62780479212fe50240ccb60dd3750b1 100644 GIT binary patch delta 332 zcmV-S0ki(~3h@jJM@dFFIbjh1rvS#W3x5F@0000X`2+y~0Dk~1000005dfzE00;h) zp#lB^{xOrG0{sL22*8t}1N;R32{DC}p#;x=_wM_5uqQA95HGv{1Q2To00$ar4j=#! zaYUcO^wejO0~%2s#l2`cjYq7Yu*}GuTQ7fH1oFosR-=z=Iz2@B$Z5z=STG;2$R7 zf(htwhk7W03kQ&a45a7}MNGj2kVt|Z5+MOBV1XwFu)+aMQ4b^l0tvEEfWi%;hZ8^_ e1zxbjF}k1-0!RP_vLr{r5#foyV?-bW0028YkaYL} delta 78 zcmey!^OcL+-P6s&GEtCW0>iV7-1Us&|NsA2{K>+|z);Vi!vF*z83y+MlNU1nV=@F% dOn;d<4ghK9KP;Svt(PV*WI4~eU6O;r8UPjW8$kd7 diff --git a/static/index.html b/static/index.html index 2bed58f98..eb41e8afd 100644 --- a/static/index.html +++ b/static/index.html @@ -1,6 +1,8 @@ + Etherpad Lite + -
      -
      New Pad

      or create/open a Pad with the name
      -
      - +
      New Pad

      or create/open a Pad with the name
      + +
      @@ -124,5 +145,4 @@ //start the costum js if(typeof costumStart == "function") costumStart(); - \ No newline at end of file diff --git a/static/js/ace.js b/static/js/ace.js index 27899aaab..69d3cf09b 100644 --- a/static/js/ace.js +++ b/static/js/ace.js @@ -1,3 +1,9 @@ +/** + * This code is mostly from the old Etherpad. Please help us to comment this code. + * This helps other people to understand this code better and helps them to improve it. + * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED + */ + /** * Copyright 2009 Google Inc. * diff --git a/static/js/ace2_common.js b/static/js/ace2_common.js index de2fede1c..1246a16ec 100644 --- a/static/js/ace2_common.js +++ b/static/js/ace2_common.js @@ -1,3 +1,9 @@ +/** + * This code is mostly from the old Etherpad. Please help us to comment this code. + * This helps other people to understand this code better and helps them to improve it. + * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED + */ + /** * Copyright 2009 Google Inc. * @@ -142,4 +148,4 @@ function htmlPrettyEscape(str) if (typeof exports !== "undefined") { exports.map = map; -} \ No newline at end of file +} diff --git a/static/js/ace2_inner.js b/static/js/ace2_inner.js index 7b0aa89f9..15c6debae 100644 --- a/static/js/ace2_inner.js +++ b/static/js/ace2_inner.js @@ -1,3 +1,9 @@ +/** + * This code is mostly from the old Etherpad. Please help us to comment this code. + * This helps other people to understand this code better and helps them to improve it. + * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED + */ + /** * Copyright 2009 Google Inc. * @@ -79,6 +85,11 @@ function OUTER(gscope) var doesWrap = true; var hasLineNumbers = true; var isStyled = true; + + // check for mobile os presence + var ua = navigator.userAgent.toLowerCase(); + var isAndroid = ua.indexOf("android") > -1; + var isMobileSafari = ua.indexOf("mobile") > -1; // space around the innermost iframe element var iframePadLeft = MIN_LINEDIV_WIDTH + LINE_NUMBER_PADDING_RIGHT + EDIT_BODY_PADDING_LEFT; @@ -1099,6 +1110,8 @@ function OUTER(gscope) else if (k == "showslinenumbers") { hasLineNumbers = !! value; + // disable line numbers on mobile devices + if(isAndroid || isMobileSafari) hasLineNumbers = false; setClassPresence(sideDiv, "sidedivhidden", !hasLineNumbers); fixView(); } @@ -1127,6 +1140,10 @@ function OUTER(gscope) { setTextSize(value); } + else if (k == 'rtlistrue') + { + setClassPresence(root, "rtl", !! value); + } } editorInfo.ace_setBaseText = function(txt) @@ -4069,8 +4086,6 @@ function OUTER(gscope) catch (e) {} if (!origSelectionRange) return false; - var selectionParent = origSelectionRange.parentElement(); - if (selectionParent.ownerDocument != doc) return false; return true; } @@ -5779,31 +5794,17 @@ function OUTER(gscope) { var newNumLines = rep.lines.length(); if (newNumLines < 1) newNumLines = 1; - if (newNumLines != lineNumbersShown) - { - var container = sideDivInner; - var odoc = outerWin.document; - while (lineNumbersShown < newNumLines) - { - lineNumbersShown++; - var n = lineNumbersShown; - var div = odoc.createElement("DIV"); - div.appendChild(odoc.createTextNode(String(n))); - container.appendChild(div); - } - while (lineNumbersShown > newNumLines) - { - container.removeChild(container.lastChild); - lineNumbersShown--; - } - } - + //update height of all current line numbers if (currentCallStack && currentCallStack.domClean) { var a = sideDivInner.firstChild; var b = doc.body.firstChild; + var n = 0; while (a && b) { + if(n > lineNumbersShown) //all updated, break + break; + var h = (b.clientHeight || b.offsetHeight); if (b.nextSibling) { @@ -5817,10 +5818,42 @@ function OUTER(gscope) if (h) { var hpx = h + "px"; - if (a.style.height != hpx) a.style.height = hpx; + if (a.style.height != hpx) { + a.style.height = hpx; + } } a = a.nextSibling; b = b.nextSibling; + n++; + } + } + + if (newNumLines != lineNumbersShown) + { + var container = sideDivInner; + var odoc = outerWin.document; + var fragment = odoc.createDocumentFragment(); + while (lineNumbersShown < newNumLines) + { + lineNumbersShown++; + var n = lineNumbersShown; + var div = odoc.createElement("DIV"); + //calculate height for new line number + var h = (b.clientHeight || b.offsetHeight); + if (b.nextSibling) + h = b.nextSibling.offsetTop - b.offsetTop; + if(h) // apply style to div + div.style.height = h +"px"; + + div.appendChild(odoc.createTextNode(String(n))); + fragment.appendChild(div); + b = b.nextSibling; + } + container.appendChild(fragment); + while (lineNumbersShown > newNumLines) + { + container.removeChild(container.lastChild); + lineNumbersShown--; } } } diff --git a/static/js/broadcast.js b/static/js/broadcast.js index 32424731b..865574abd 100644 --- a/static/js/broadcast.js +++ b/static/js/broadcast.js @@ -1,3 +1,9 @@ +/** + * This code is mostly from the old Etherpad. Please help us to comment this code. + * This helps other people to understand this code better and helps them to improve it. + * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED + */ + /** * Copyright 2009 Google Inc. * diff --git a/static/js/broadcast_revisions.js b/static/js/broadcast_revisions.js index 828677733..33a2f9dfa 100644 --- a/static/js/broadcast_revisions.js +++ b/static/js/broadcast_revisions.js @@ -1,3 +1,9 @@ +/** + * This code is mostly from the old Etherpad. Please help us to comment this code. + * This helps other people to understand this code better and helps them to improve it. + * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED + */ + /** * Copyright 2009 Google Inc. * diff --git a/static/js/broadcast_slider.js b/static/js/broadcast_slider.js index 68f424495..af5a50411 100644 --- a/static/js/broadcast_slider.js +++ b/static/js/broadcast_slider.js @@ -1,3 +1,9 @@ +/** + * This code is mostly from the old Etherpad. Please help us to comment this code. + * This helps other people to understand this code better and helps them to improve it. + * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED + */ + /** * Copyright 2009 Google Inc. * diff --git a/static/js/changesettracker.js b/static/js/changesettracker.js index 09e413f39..cc7f64623 100644 --- a/static/js/changesettracker.js +++ b/static/js/changesettracker.js @@ -1,3 +1,9 @@ +/** + * This code is mostly from the old Etherpad. Please help us to comment this code. + * This helps other people to understand this code better and helps them to improve it. + * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED + */ + /** * Copyright 2009 Google Inc. * diff --git a/static/js/chat.js b/static/js/chat.js index 1bc44d201..4dade69f3 100644 --- a/static/js/chat.js +++ b/static/js/chat.js @@ -1,3 +1,9 @@ +/** + * This code is mostly from the old Etherpad. Please help us to comment this code. + * This helps other people to understand this code better and helps them to improve it. + * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED + */ + /** * Copyright 2009 Google Inc., 2011 Peter 'Pita' Martischka (Primary Technology Ltd) * @@ -16,21 +22,33 @@ var chat = (function() { + var ua = navigator.userAgent.toLowerCase(); + var isAndroid = ua.indexOf("android") > -1; + var isMobileSafari = ua.indexOf("mobile") > -1; + var bottomMargin = "0px"; + var sDuration = 500; + var hDuration = 750; var chatMentions = 0; var title = document.title; + if (isAndroid || isMobileSafari){ + sDuration = 0; + hDuration = 0; + } var self = { show: function () { $("#chaticon").hide("slide", { direction: "down" - }, 500, function () + }, hDuration, function () { $("#chatbox").show("slide", { direction: "down" - }, 750, self.scrollDown); + }, sDuration, self.scrollDown); $("#chatbox").resizable( { handles: 'nw', + minHeight: 40, + minWidth: 80, start: function (event, ui) { $("#focusprotector").show(); @@ -39,7 +57,10 @@ var chat = (function() { $("#focusprotector").hide(); - $("#chatbox").css({right: "20px", bottom: "0px", left: "", top: ""}); + if(isAndroid || isMobileSafari) + bottommargin = "32px"; + + $("#chatbox").css({right: "20px", bottom: bottomMargin, left: "", top: ""}); self.scrollDown(); } @@ -51,9 +72,9 @@ var chat = (function() hide: function () { $("#chatcounter").text("0"); - $("#chatbox").hide("slide", { direction: "down" }, 750, function() + $("#chatbox").hide("slide", { direction: "down" }, sDuration, function() { - $("#chaticon").show("slide", { direction: "down" }, 500); + $("#chaticon").show("slide", { direction: "down" }, hDuration); }); }, scrollDown: function() diff --git a/static/js/collab_client.js b/static/js/collab_client.js index c64b99949..fafe0fd2f 100644 --- a/static/js/collab_client.js +++ b/static/js/collab_client.js @@ -1,3 +1,9 @@ +/** + * This code is mostly from the old Etherpad. Please help us to comment this code. + * This helps other people to understand this code better and helps them to improve it. + * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED + */ + /** * Copyright 2009 Google Inc. * diff --git a/static/js/colorutils.js b/static/js/colorutils.js index 03141271b..92d8da719 100644 --- a/static/js/colorutils.js +++ b/static/js/colorutils.js @@ -1,3 +1,9 @@ +/** + * This code is mostly from the old Etherpad. Please help us to comment this code. + * This helps other people to understand this code better and helps them to improve it. + * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED + */ + // DO NOT EDIT THIS FILE, edit infrastructure/ace/www/colorutils.js // THIS FILE IS ALSO SERVED AS CLIENT-SIDE JS /** diff --git a/static/js/contentcollector.js b/static/js/contentcollector.js index ac7e34028..883ca09f0 100644 --- a/static/js/contentcollector.js +++ b/static/js/contentcollector.js @@ -1,3 +1,9 @@ +/** + * This code is mostly from the old Etherpad. Please help us to comment this code. + * This helps other people to understand this code better and helps them to improve it. + * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED + */ + // THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.contentcollector // %APPJET%: import("etherpad.collab.ace.easysync2.Changeset"); // %APPJET%: import("etherpad.admin.plugins"); diff --git a/static/js/cssmanager.js b/static/js/cssmanager.js index 5601ea25b..b8208b83f 100644 --- a/static/js/cssmanager.js +++ b/static/js/cssmanager.js @@ -1,3 +1,9 @@ +/** + * This code is mostly from the old Etherpad. Please help us to comment this code. + * This helps other people to understand this code better and helps them to improve it. + * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED + */ + /** * Copyright 2009 Google Inc. * diff --git a/static/js/cssmanager_client.js b/static/js/cssmanager_client.js index 2fa61027e..60bf9f8c5 100644 --- a/static/js/cssmanager_client.js +++ b/static/js/cssmanager_client.js @@ -1,3 +1,9 @@ +/** + * This code is mostly from the old Etherpad. Please help us to comment this code. + * This helps other people to understand this code better and helps them to improve it. + * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED + */ + // DO NOT EDIT THIS FILE, edit infrastructure/ace/www/cssmanager.js /** * Copyright 2009 Google Inc. diff --git a/static/js/domline.js b/static/js/domline.js index f081f7df3..0b9abd3f1 100644 --- a/static/js/domline.js +++ b/static/js/domline.js @@ -1,3 +1,9 @@ +/** + * This code is mostly from the old Etherpad. Please help us to comment this code. + * This helps other people to understand this code better and helps them to improve it. + * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED + */ + // THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.domline // %APPJET%: import("etherpad.admin.plugins"); /** diff --git a/static/js/domline_client.js b/static/js/domline_client.js index b37996d92..9f47dae54 100644 --- a/static/js/domline_client.js +++ b/static/js/domline_client.js @@ -1,3 +1,9 @@ +/** + * This code is mostly from the old Etherpad. Please help us to comment this code. + * This helps other people to understand this code better and helps them to improve it. + * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED + */ + // DO NOT EDIT THIS FILE, edit infrastructure/ace/www/domline.js // THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.domline /** diff --git a/static/js/draggable.js b/static/js/draggable.js index 2f40c1c7e..7bf523eb5 100644 --- a/static/js/draggable.js +++ b/static/js/draggable.js @@ -1,3 +1,9 @@ +/** + * This code is mostly from the old Etherpad. Please help us to comment this code. + * This helps other people to understand this code better and helps them to improve it. + * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED + */ + /** * Copyright 2009 Google Inc. * diff --git a/static/js/easysync2.js b/static/js/easysync2.js index 7f5d0cf98..17ab585d9 100644 --- a/static/js/easysync2.js +++ b/static/js/easysync2.js @@ -1,3 +1,9 @@ +/** + * This code is mostly from the old Etherpad. Please help us to comment this code. + * This helps other people to understand this code better and helps them to improve it. + * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED + */ + // THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.easysync2 // %APPJET%: jimport("com.etherpad.Easysync2Support"); /** diff --git a/static/js/easysync2_client.js b/static/js/easysync2_client.js index 5d0e582d8..3611da233 100644 --- a/static/js/easysync2_client.js +++ b/static/js/easysync2_client.js @@ -1,3 +1,9 @@ +/** + * This code is mostly from the old Etherpad. Please help us to comment this code. + * This helps other people to understand this code better and helps them to improve it. + * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED + */ + // DO NOT EDIT THIS FILE, edit infrastructure/ace/www/easysync2.js // THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.easysync2 /** diff --git a/static/js/linestylefilter.js b/static/js/linestylefilter.js index ace55f38d..13a746186 100644 --- a/static/js/linestylefilter.js +++ b/static/js/linestylefilter.js @@ -1,3 +1,9 @@ +/** + * This code is mostly from the old Etherpad. Please help us to comment this code. + * This helps other people to understand this code better and helps them to improve it. + * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED + */ + // THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.linestylefilter // %APPJET%: import("etherpad.collab.ace.easysync2.Changeset"); // %APPJET%: import("etherpad.admin.plugins"); diff --git a/static/js/linestylefilter_client.js b/static/js/linestylefilter_client.js index 9c3ae01c9..9fd2a3f8c 100644 --- a/static/js/linestylefilter_client.js +++ b/static/js/linestylefilter_client.js @@ -1,3 +1,9 @@ +/** + * This code is mostly from the old Etherpad. Please help us to comment this code. + * This helps other people to understand this code better and helps them to improve it. + * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED + */ + // DO NOT EDIT THIS FILE, edit infrastructure/ace/www/linestylefilter.js // THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.linestylefilter /** diff --git a/static/js/pad2.js b/static/js/pad2.js index 6f398bade..bbb385b3b 100644 --- a/static/js/pad2.js +++ b/static/js/pad2.js @@ -1,3 +1,9 @@ +/** + * This code is mostly from the old Etherpad. Please help us to comment this code. + * This helps other people to understand this code better and helps them to improve it. + * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED + */ + /** * Copyright 2009 Google Inc., 2011 Peter 'Pita' Martischka (Primary Technology Ltd) * @@ -21,6 +27,8 @@ var LineNumbersDisabled = false; var noColors = false; var useMonospaceFontGlobal = false; var globalUserName = false; +var hideQRCode = false; +var rtlIsTrue = false; $(document).ready(function() { @@ -86,12 +94,15 @@ function getParams() var showLineNumbers = params["showLineNumbers"]; var useMonospaceFont = params["useMonospaceFont"]; var IsnoColors = params["noColors"]; + var hideQRCode = params["hideQRCode"]; + var rtl = params["rtl"]; if(IsnoColors) { if(IsnoColors == "true") { noColors = true; + $('#clearAuthorship').hide(); } } if(showControls) @@ -102,7 +113,6 @@ function getParams() $('#editorcontainer').css({"top":"0px"}); } } - if(showChat) { if(showChat == "false") @@ -110,7 +120,6 @@ function getParams() $('#chaticon').hide(); } } - if(showLineNumbers) { if(showLineNumbers == "false") @@ -118,7 +127,6 @@ function getParams() LineNumbersDisabled = true; } } - if(useMonospaceFont) { if(useMonospaceFont == "true") @@ -126,13 +134,22 @@ function getParams() useMonospaceFontGlobal = true; } } - - if(userName) { // If the username is set as a parameter we should set a global value that we can call once we have initiated the pad. globalUserName = unescape(userName); } + if(hideQRCode) + { + $('#qrcode').hide(); + } + if(rtl) + { + if(rtl == "true") + { + rtlIsTrue = true + } + } } function getUrlVars() @@ -177,7 +194,7 @@ function handshake() padId = decodeURIComponent(padId); // unescape neccesary due to Safari and Opera interpretation of spaces if(!isReconnect) - document.title = document.title + " | " + padId; + document.title = document.title + " | " + padId.replace(/_+/g, ' '); var token = readCookie("token"); if (token == null) @@ -291,6 +308,11 @@ function handshake() { pad.changeViewOption('noColors', true); } + + if (rtlIsTrue == true) + { + pad.changeViewOption('rtl', true); + } // If the Monospacefont value is set to true then change it to monospace. if (useMonospaceFontGlobal == true) diff --git a/static/js/pad_connectionstatus.js b/static/js/pad_connectionstatus.js index 3d244303c..d35eb02d6 100644 --- a/static/js/pad_connectionstatus.js +++ b/static/js/pad_connectionstatus.js @@ -1,3 +1,9 @@ +/** + * This code is mostly from the old Etherpad. Please help us to comment this code. + * This helps other people to understand this code better and helps them to improve it. + * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED + */ + /** * Copyright 2009 Google Inc. * diff --git a/static/js/pad_cookie.js b/static/js/pad_cookie.js index 5db6a2ca0..64211750d 100644 --- a/static/js/pad_cookie.js +++ b/static/js/pad_cookie.js @@ -1,3 +1,9 @@ +/** + * This code is mostly from the old Etherpad. Please help us to comment this code. + * This helps other people to understand this code better and helps them to improve it. + * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED + */ + /** * Copyright 2009 Google Inc. * diff --git a/static/js/pad_docbar.js b/static/js/pad_docbar.js index 6ee38f686..c67ca55c4 100644 --- a/static/js/pad_docbar.js +++ b/static/js/pad_docbar.js @@ -1,3 +1,9 @@ +/** + * This code is mostly from the old Etherpad. Please help us to comment this code. + * This helps other people to understand this code better and helps them to improve it. + * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED + */ + /** * Copyright 2009 Google Inc. * diff --git a/static/js/pad_editbar.js b/static/js/pad_editbar.js index 3818db871..6cd5163de 100644 --- a/static/js/pad_editbar.js +++ b/static/js/pad_editbar.js @@ -1,3 +1,9 @@ +/** + * This code is mostly from the old Etherpad. Please help us to comment this code. + * This helps other people to understand this code better and helps them to improve it. + * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED + */ + /** * Copyright 2009 Google Inc. * @@ -151,7 +157,7 @@ var padeditbar = (function() }, cmd, true); } } - padeditor.ace.focus(); + if(padeditor.ace) padeditor.ace.focus(); }, toogleDropDown: function(moduleName) { diff --git a/static/js/pad_editor.js b/static/js/pad_editor.js index e98252cff..183de948d 100644 --- a/static/js/pad_editor.js +++ b/static/js/pad_editor.js @@ -1,3 +1,9 @@ +/** + * This code is mostly from the old Etherpad. Please help us to comment this code. + * This helps other people to understand this code better and helps them to improve it. + * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED + */ + /** * Copyright 2009 Google Inc. * @@ -89,6 +95,7 @@ var padeditor = (function() self.ace.setProperty("showsauthorcolors", noColors); + self.ace.setProperty("rtlIsTrue", rtlIsTrue); }, initViewZoom: function() { diff --git a/static/js/pad_impexp.js b/static/js/pad_impexp.js index 8224fce56..2f519a1ec 100644 --- a/static/js/pad_impexp.js +++ b/static/js/pad_impexp.js @@ -1,3 +1,9 @@ +/** + * This code is mostly from the old Etherpad. Please help us to comment this code. + * This helps other people to understand this code better and helps them to improve it. + * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED + */ + /** * Copyright 2009 Google Inc. * @@ -230,10 +236,16 @@ var padimpexp = (function() var self = { init: function() { + //get /p/padname + var pad_root_path = new RegExp(/.*\/p\/[^\/]+/).exec(document.location.pathname) + //get http://example.com/p/padname + var pad_root_url = document.location.href.replace(document.location.pathname, pad_root_path) + // build the export links - $("#exporthtmla").attr("href", document.location.pathname + "/export/html"); - $("#exportplaina").attr("href", document.location.pathname + "/export/txt"); - $("#exportwordlea").attr("href", document.location.pathname + "/export/wordle"); + $("#exporthtmla").attr("href", pad_root_path + "/export/html"); + $("#exportplaina").attr("href", pad_root_path + "/export/txt"); + $("#exportwordlea").attr("href", pad_root_path + "/export/wordle"); + $("#exportdokuwikia").attr("href", pad_root_path + "/export/dokuwiki"); //hide stuff thats not avaible if abiword is disabled if(clientVars.abiwordAvailable == "no") @@ -241,29 +253,29 @@ var padimpexp = (function() $("#exportworda").remove(); $("#exportpdfa").remove(); $("#exportopena").remove(); - $("#importexport").css({"height":"95px"}); - $("#importexportline").css({"height":"95px"}); + $("#importexport").css({"height":"115px"}); + $("#importexportline").css({"height":"115px"}); $("#import").html("Import is not available"); } else if(clientVars.abiwordAvailable == "withoutPDF") { $("#exportpdfa").remove(); - $("#exportworda").attr("href", document.location.pathname + "/export/doc"); - $("#exportopena").attr("href", document.location.pathname + "/export/odt"); + $("#exportworda").attr("href", pad_root_path + "/export/doc"); + $("#exportopena").attr("href", pad_root_path + "/export/odt"); $("#importexport").css({"height":"142px"}); $("#importexportline").css({"height":"142px"}); - $("#importform").get(0).setAttribute('action', document.location.href + "/import"); + $("#importform").attr('action', pad_root_url + "/import"); } else { - $("#exportworda").attr("href", document.location.pathname + "/export/doc"); - $("#exportpdfa").attr("href", document.location.pathname + "/export/pdf"); - $("#exportopena").attr("href", document.location.pathname + "/export/odt"); + $("#exportworda").attr("href", pad_root_path + "/export/doc"); + $("#exportpdfa").attr("href", pad_root_path + "/export/pdf"); + $("#exportopena").attr("href", pad_root_path + "/export/odt"); - $("#importform").get(0).setAttribute('action', document.location.pathname + "/import"); + $("#importform").attr('action', pad_root_path + "/import"); } $("#impexp-close").click(function() @@ -301,7 +313,7 @@ var padimpexp = (function() }, export2Wordle: function() { - var padUrl = document.location.href + "/export/txt"; + var padUrl = $('#exportwordlea').attr('href').replace(/\/wordle$/, '/txt') $.get(padUrl, function(data) { diff --git a/static/js/pad_modals.js b/static/js/pad_modals.js index ccd4607ec..cf9d04355 100644 --- a/static/js/pad_modals.js +++ b/static/js/pad_modals.js @@ -1,3 +1,9 @@ +/** + * This code is mostly from the old Etherpad. Please help us to comment this code. + * This helps other people to understand this code better and helps them to improve it. + * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED + */ + /** * Copyright 2009 Google Inc. * diff --git a/static/js/pad_savedrevs.js b/static/js/pad_savedrevs.js index ae4220cd9..487e85f1a 100644 --- a/static/js/pad_savedrevs.js +++ b/static/js/pad_savedrevs.js @@ -1,3 +1,9 @@ +/** + * This code is mostly from the old Etherpad. Please help us to comment this code. + * This helps other people to understand this code better and helps them to improve it. + * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED + */ + /** * Copyright 2009 Google Inc. * diff --git a/static/js/pad_userlist.js b/static/js/pad_userlist.js index 8680fc2e6..748674916 100644 --- a/static/js/pad_userlist.js +++ b/static/js/pad_userlist.js @@ -1,3 +1,9 @@ +/** + * This code is mostly from the old Etherpad. Please help us to comment this code. + * This helps other people to understand this code better and helps them to improve it. + * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED + */ + /** * Copyright 2009 Google Inc. * @@ -711,7 +717,14 @@ var paduserlist = (function() } $("#myswatch").css({'background-color': myUserInfo.colorId}); - $("#usericon").css({'box-shadow': 'inset 0 0 30px ' + myUserInfo.colorId}); + + if ($.browser.msie && parseInt($.browser.version) <= 8) { + $("#usericon").css({'box-shadow': 'inset 0 0 30px ' + myUserInfo.colorId,'background-color': myUserInfo.colorId}); + } + else + { + $("#usericon").css({'box-shadow': 'inset 0 0 30px ' + myUserInfo.colorId}); + } } }; return self; diff --git a/static/js/pad_utils.js b/static/js/pad_utils.js index 331590fed..76a167057 100644 --- a/static/js/pad_utils.js +++ b/static/js/pad_utils.js @@ -1,3 +1,9 @@ +/** + * This code is mostly from the old Etherpad. Please help us to comment this code. + * This helps other people to understand this code better and helps them to improve it. + * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED + */ + /** * Copyright 2009 Google Inc. * diff --git a/static/js/plugins.js b/static/js/plugins.js index 2ebf98f3c..b29d01d4d 100644 --- a/static/js/plugins.js +++ b/static/js/plugins.js @@ -1,3 +1,9 @@ +/** + * This code is mostly from the old Etherpad. Please help us to comment this code. + * This helps other people to understand this code better and helps them to improve it. + * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED + */ + plugins = { callHook: function(hookName, args) { diff --git a/static/js/skiplist.js b/static/js/skiplist.js index c21873887..c9654be46 100644 --- a/static/js/skiplist.js +++ b/static/js/skiplist.js @@ -1,3 +1,9 @@ +/** + * This code is mostly from the old Etherpad. Please help us to comment this code. + * This helps other people to understand this code better and helps them to improve it. + * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED + */ + /** * Copyright 2009 Google Inc. * diff --git a/static/js/undo-xpopup.js b/static/js/undo-xpopup.js index 2fc0ee1a7..e733f3ea3 100644 --- a/static/js/undo-xpopup.js +++ b/static/js/undo-xpopup.js @@ -1,3 +1,9 @@ +/** + * This code is mostly from the old Etherpad. Please help us to comment this code. + * This helps other people to understand this code better and helps them to improve it. + * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED + */ + /** * Copyright 2009 Google Inc. * diff --git a/static/js/undomodule.js b/static/js/undomodule.js index b070ce253..3891629aa 100644 --- a/static/js/undomodule.js +++ b/static/js/undomodule.js @@ -1,3 +1,9 @@ +/** + * This code is mostly from the old Etherpad. Please help us to comment this code. + * This helps other people to understand this code better and helps them to improve it. + * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED + */ + /** * Copyright 2009 Google Inc. * diff --git a/static/js/virtual_lines.js b/static/js/virtual_lines.js index e27e4af40..ece96b149 100644 --- a/static/js/virtual_lines.js +++ b/static/js/virtual_lines.js @@ -1,3 +1,9 @@ +/** + * This code is mostly from the old Etherpad. Please help us to comment this code. + * This helps other people to understand this code better and helps them to improve it. + * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED + */ + /** * Copyright 2009 Google Inc. * diff --git a/static/pad.html b/static/pad.html index 7565993a4..8903847fd 100644 --- a/static/pad.html +++ b/static/pad.html @@ -4,12 +4,14 @@ Etherpad Lite + @@ -23,60 +25,60 @@
      @@ -230,7 +233,7 @@

      - +
      diff --git a/static/timeslider.html b/static/timeslider.html index 11c5ef7f4..c1310cc6c 100644 --- a/static/timeslider.html +++ b/static/timeslider.html @@ -53,7 +53,7 @@ return "t." + randomstring; } - var socket, token, padId; + var socket, token, padId, export_links; $(document).ready(function () { @@ -65,7 +65,7 @@ padId = decodeURIComponent(urlParts[urlParts.length-2]); //set the title - document.title = document.title + " | " + padId; + document.title = document.title + " | " + padId.replace(/_+/g, ' '); //ensure we have a token token = readCookie("token"); @@ -110,6 +110,9 @@ $("body").html("

      You have no permission to access this pad

      ") } }); + + //get all the export links + export_links = $('#export > .exportlink') }); //sends a message over the socket @@ -141,6 +144,19 @@ loadBroadcastSliderJS(); loadBroadcastRevisionsJS(); loadBroadcastJS(); + + //initialize export ui + padimpexp.init(); + + //change export urls when the slider moves + var export_rev_regex = /(\/\d+)?\/export/ + BroadcastSlider.onSlider(function(revno) + { + export_links.each(function() + { + this.setAttribute('href', this.href.replace(export_rev_regex, '/' + revno + '/export')); + }); + }); //fire all start functions of these scripts, formerly fired with window.load for(var i=0;i < fireWhenAllScriptsAreLoaded.length;i++) @@ -269,6 +285,13 @@
      + Return to pad