diff --git a/.gitignore b/.gitignore index ab91434bd..50cd6e212 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,6 @@ bin/abiword.exe bin/node.exe etherpad-lite-win.zip var/dirty.db -bin/convertSettings.json \ No newline at end of file +bin/convertSettings.json +*~ +*.patch \ No newline at end of file diff --git a/README.md b/README.md index 6b4e1e956..ab241333f 100644 --- a/README.md +++ b/README.md @@ -67,12 +67,11 @@ Here is the **[FAQ](https://github.com/Pita/etherpad-lite/wiki/FAQ)**
  • Move into the node folder cd node-v0.6* and build node with ./configure && make && make install
  • -
  • Install npm curl http://npmjs.org/install.sh | sh
  • **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. @@ -115,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..a49c46b13 100755 --- a/bin/installDeps.sh +++ b/bin/installDeps.sh @@ -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..1cfa203c8 100644 --- a/node/db/API.js +++ b/node/db/API.js @@ -18,6 +18,7 @@ * limitations under the License. */ +var ERR = require("async-stacktrace"); var padManager = require("./PadManager"); var padMessageHandler = require("../handler/PadMessageHandler"); var readOnlyManager = require("./ReadOnlyManager"); @@ -109,11 +110,7 @@ exports.getText = function(padID, rev, callback) //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) @@ -128,12 +125,11 @@ exports.getText = function(padID, rev, callback) //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 +154,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); @@ -215,11 +207,7 @@ exports.getHTML = function(padID, rev, callback) 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) @@ -234,11 +222,9 @@ exports.getHTML = function(padID, rev, callback) //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 +232,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 +247,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 +275,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()}); }); @@ -323,7 +301,8 @@ exports.createPad = function(padID, text, callback) //create pad getPadSafe(padID, false, text, function(err) { - callback(err); + if(ERR(err, callback)) return; + callback(); }); } @@ -339,11 +318,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 +337,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}); }); }); } @@ -396,11 +368,7 @@ exports.setPublicStatus = function(padID, publicStatus, callback) //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") @@ -433,11 +401,7 @@ exports.getPublicStatus = function(padID, callback) //get the pad getPadSafe(padID, true, function(err, pad) { - if(err) - { - callback(err); - return; - } + if(ERR(err, callback)) return; callback(null, {publicStatus: pad.getPublicStatus()}); }); @@ -463,11 +427,7 @@ exports.setPassword = function(padID, password, callback) //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); @@ -496,11 +456,7 @@ exports.isPasswordProtected = function(padID, callback) //get the pad getPadSafe(padID, true, function(err, pad) { - if(err) - { - callback(err); - return; - } + if(ERR(err, callback)) return; callback(null, {isPasswordProtected: pad.isPasswordProtected()}); }); @@ -542,13 +498,10 @@ function getPadSafe(padID, shouldExist, text, callback) //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"}); } 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..a87d5d14f 100644 --- a/node/db/GroupManager.js +++ b/node/db/GroupManager.js @@ -18,6 +18,7 @@ * limitations under the License. */ +var ERR = require("async-stacktrace"); var db = require("./DB").db; var async = require("async"); var padManager = require("./PadManager"); @@ -34,13 +35,10 @@ 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"}); } @@ -67,7 +65,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 +77,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 +105,8 @@ exports.deleteGroup = function(groupID, callback) } ], function(err) { - callback(err); + if(ERR(err, callback)) return; + callback(); }); } @@ -116,7 +115,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); }); } @@ -142,23 +142,14 @@ exports.createGroupIfNotExistsFor = function(groupMapper, callback) //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 +160,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,13 +177,10 @@ 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"}); } @@ -207,13 +196,10 @@ 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"}); } @@ -229,7 +215,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 +227,8 @@ exports.createGroupPad = function(groupID, padName, text, callback) } ], function(err) { - callback(err, {padID: padID}); + if(ERR(err, callback)) return; + callback(null, {padID: padID}); }); } @@ -248,13 +236,10 @@ 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"}); } @@ -263,7 +248,8 @@ exports.listPads = function(groupID, callback) { 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..46eb3c966 100644 --- a/node/db/PadManager.js +++ b/node/db/PadManager.js @@ -18,6 +18,7 @@ * limitations under the License. */ +var ERR = require("async-stacktrace"); require("../db/Pad"); var db = require("./DB").db; @@ -90,15 +91,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,7 +104,8 @@ 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); }); } 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..a804d8c3a 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 @@ -118,7 +124,7 @@ exports.checkAccess = function (padID, sessionID, token, password, 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..9279273f5 100644 --- a/node/db/SessionManager.js +++ b/node/db/SessionManager.js @@ -18,6 +18,7 @@ * limitations under the License. */ +var ERR = require("async-stacktrace"); var db = require("./DB").db; var async = require("async"); var groupMangager = require("./GroupManager"); @@ -28,7 +29,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,13 +47,10 @@ 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"}); } @@ -67,13 +66,10 @@ 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"}); } @@ -137,12 +133,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 +156,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 +175,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,13 +187,10 @@ 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"}) } @@ -231,13 +216,10 @@ 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"}) } @@ -256,8 +238,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 +248,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 +271,8 @@ exports.deleteSession = function(sessionID, callback) } ], function(err) { - callback(err); + if(ERR(err, callback)) return; + callback(); }) } @@ -295,13 +280,10 @@ 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"}); } @@ -317,13 +299,10 @@ 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"}); } @@ -346,8 +325,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 +344,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..8dc68378f 100644 --- a/node/handler/APIHandler.js +++ b/node/handler/APIHandler.js @@ -18,6 +18,7 @@ * limitations under the License. */ +var ERR = require("async-stacktrace"); var fs = require("fs"); var api = require("../db/API"); @@ -122,7 +123,7 @@ exports.handle = function(functionName, fields, req, res) else { res.send({code: 2, message: "internal error", data: null}); - throw (err); + ERR(err); } }); diff --git a/node/handler/ExportHandler.js b/node/handler/ExportHandler.js index 8cc74747d..092198ed5 100644 --- a/node/handler/ExportHandler.js +++ b/node/handler/ExportHandler.js @@ -18,6 +18,7 @@ * limitations under the License. */ +var ERR = require("async-stacktrace"); var exporthtml = require("../utils/ExportHtml"); var padManager = require("../db/PadManager"); var async = require("async"); @@ -50,8 +51,7 @@ exports.doExport = function(req, res, padId, type) { padManager.getPad(padId, function(err, pad) { - if(err) - throw err; + ERR(err); res.send(pad.text()); }); @@ -68,8 +68,9 @@ exports.doExport = function(req, res, padId, type) { exporthtml.getPadHTMLDocument(padId, null, false, function(err, _html) { + if(ERR(err, callback)) return; html = _html; - callback(err); + callback(); }); }, //decide what to do with the html export @@ -113,19 +114,23 @@ exports.doExport = function(req, res, padId, type) function(callback) { //100ms delay to accomidate for slow windows fs - if(os.type().indexOf("Windows") > -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..d86528498 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"); @@ -107,7 +108,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 +219,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 +260,7 @@ function handleChatMessage(client, message) } ], function(err) { - if(err) throw err; + ERR(err); }); } @@ -375,8 +378,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 +426,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 +467,7 @@ function handleUserChanges(client, message) } ], function(err) { - if(err) throw err; + ERR(err); }); } @@ -502,25 +500,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 +629,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 +653,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 +663,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 +701,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 +713,9 @@ function handleClientReady(client, message) { pad.getLastChatMessages(100, function(err, _chatMessages) { + if(ERR(err, callback)) return; chatMessages = _chatMessages; - callback(err); + callback(); }); } ], callback); @@ -806,24 +808,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 +863,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 +909,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..b3493d8cb 100644 --- a/node/handler/TimesliderMessageHandler.js +++ b/node/handler/TimesliderMessageHandler.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"); @@ -92,7 +93,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 +139,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; @@ -171,8 +172,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 +189,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 +207,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 +240,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 +274,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 +317,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 +330,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 +341,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 +394,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 +422,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 +434,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 +453,8 @@ function getPadLines(padId, revNum, callback) } ], function(err) { - callback(err, result); + if(ERR(err, callback)) return; + callback(null, result); }); } @@ -465,8 +474,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 +496,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 +520,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..ae75149c9 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); diff --git a/node/utils/ExportHtml.js b/node/utils/ExportHtml.js index 782bb7016..46ed980a5 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); }); } @@ -410,11 +413,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') + '\n' + (noDocType ? '' : '\n' + '\n' + '\n' + '\n') + ''; @@ -422,7 +421,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..fd1dd0793 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'); @@ -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: none; + left: 30px; + } + #mycolorpicker { + right: 0; + left: 0 !important; + } + #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; + } + #sidediv { + display:none; + } +} \ No newline at end of file diff --git a/static/index.html b/static/index.html index c353fbb0c..f5f250b5a 100644 --- a/static/index.html +++ b/static/index.html @@ -1,6 +1,8 @@ + RPG Write + -
        -
        New Pad

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

        or create/open a Pad with the name
        + +
        @@ -126,3 +147,4 @@ + diff --git a/static/js/ace2_common.js b/static/js/ace2_common.js index 184785863..de2fede1c 100644 --- a/static/js/ace2_common.js +++ b/static/js/ace2_common.js @@ -76,10 +76,13 @@ function isArray(testObject) if (typeof exports !== "undefined") { - var navigator = {userAgent: "node-js"}; + userAgent = "node-js"; +} +else +{ + userAgent = navigator.userAgent.toLowerCase(); } // Figure out what browser is being used (stolen from jquery 1.2.1) -userAgent = navigator.userAgent.toLowerCase(); var browser = { version: (userAgent.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/) || [])[1], safari: /webkit/.test(userAgent), diff --git a/static/js/ace2_inner.js b/static/js/ace2_inner.js index 1575fc9ad..bb45ac5f9 100644 --- a/static/js/ace2_inner.js +++ b/static/js/ace2_inner.js @@ -3534,7 +3534,8 @@ function OUTER(gscope) function doIndentOutdent(isOut) { - if (!(rep.selStart && rep.selEnd)) + if (!(rep.selStart && rep.selEnd) || + ((rep.selStart[0] == rep.selEnd[0]) && (rep.selStart[1] == rep.selEnd[1]) && rep.selEnd[1] > 1)) { return false; } @@ -3544,25 +3545,25 @@ function OUTER(gscope) lastLine = Math.max(firstLine, rep.selEnd[0] - ((rep.selEnd[1] == 0) ? 1 : 0)); var mods = []; - var foundLists = false; for (var n = firstLine; n <= lastLine; n++) { var listType = getLineListType(n); + var t = 'indent'; + var level = 0; if (listType) { listType = /([a-z]+)([12345678])/.exec(listType); if (listType) { - foundLists = true; - var t = listType[1]; - var level = Number(listType[2]); - var newLevel = Math.max(1, Math.min(MAX_LIST_LEVEL, level + (isOut ? -1 : 1))); - if (level != newLevel) - { - mods.push([n, t + newLevel]); - } + t = listType[1]; + level = Number(listType[2]); } } + var newLevel = Math.max(0, Math.min(MAX_LIST_LEVEL, level + (isOut ? -1 : 1))); + if (level != newLevel) + { + mods.push([n, (newLevel > 0) ? t + newLevel : '']); + } } if (mods.length > 0) @@ -3570,7 +3571,7 @@ function OUTER(gscope) setLineListTypes(mods); } - return foundLists; + return true; } editorInfo.ace_doIndentOutdent = doIndentOutdent; @@ -5231,7 +5232,8 @@ function OUTER(gscope) var allLinesAreList = true; for (var n = firstLine; n <= lastLine; n++) { - if (!getLineListType(n)) + var listType = getLineListType(n); + if (!listType || listType.slice(0, 'bullet'.length) != 'bullet') { allLinesAreList = false; break; @@ -5241,8 +5243,16 @@ function OUTER(gscope) var mods = []; for (var n = firstLine; n <= lastLine; n++) { + var t = ''; + var level = 0; + var listType = /([a-z]+)([12345678])/.exec(getLineListType(n)); + if (listType) + { + t = listType[1]; + level = Number(listType[2]); + } var t = getLineListType(n); - mods.push([n, allLinesAreList ? '' : (t ? t : 'bullet1')]); + mods.push([n, allLinesAreList ? 'indent' + level : (t ? 'bullet' + level : 'bullet1')]); } setLineListTypes(mods); } @@ -5647,31 +5657,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) { @@ -5685,10 +5681,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/chat.js b/static/js/chat.js index 1bc44d201..bda5786de 100644 --- a/static/js/chat.js +++ b/static/js/chat.js @@ -16,6 +16,10 @@ var chat = (function() { + var ua = navigator.userAgent.toLowerCase(); + var isAndroid = ua.indexOf("android") > -1; + var isMobileSafari = ua.indexOf("mobile") > -1; + var bottomMargin = "0px"; var chatMentions = 0; var title = document.title; var self = { @@ -31,6 +35,8 @@ var chat = (function() $("#chatbox").resizable( { handles: 'nw', + minHeight: 40, + minWidth: 80, start: function (event, ui) { $("#focusprotector").show(); @@ -39,7 +45,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,10 +60,14 @@ var chat = (function() hide: function () { $("#chatcounter").text("0"); - $("#chatbox").hide("slide", { direction: "down" }, 750, function() + if(isAndroid || isMobileSafari) { + $("#chatbox").toggle(); + } + else { - $("#chaticon").show("slide", { direction: "down" }, 500); - }); + $("#chatbox").toggle("slide", { direction: "down" }, 625); + } + }, scrollDown: function() { @@ -153,4 +166,4 @@ var chat = (function() } return self; -}()); +}()); \ No newline at end of file diff --git a/static/js/contentcollector.js b/static/js/contentcollector.js index a776affec..577b24c6c 100644 --- a/static/js/contentcollector.js +++ b/static/js/contentcollector.js @@ -473,7 +473,7 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class if (tname == "ul") { var type; - var rr = cls && /(?:^| )list-(bullet[12345678])\b/.exec(cls); + var rr = cls && /(?:^| )list-([a-z]+[12345678])\b/.exec(cls); type = rr && rr[1] || "bullet" + String(Math.min(_MAX_LIST_LEVEL, (state.listNesting || 0) + 1)); oldListTypeOrNull = (_enterList(state, type) || 'none'); } diff --git a/static/js/pad2.js b/static/js/pad2.js index d194b6607..debd25512 100644 --- a/static/js/pad2.js +++ b/static/js/pad2.js @@ -21,6 +21,7 @@ var LineNumbersDisabled = false; var noColors = false; var useMonospaceFontGlobal = false; var globalUserName = false; +var hideQRCode = false; $(document).ready(function() { @@ -86,12 +87,14 @@ function getParams() var showLineNumbers = params["showLineNumbers"]; var useMonospaceFont = params["useMonospaceFont"]; var IsnoColors = params["noColors"]; + var hideQRCode = params["hideQRCode"]; if(IsnoColors) { if(IsnoColors == "true") { noColors = true; + $('#clearAuthorship').hide(); } } if(showControls) @@ -102,7 +105,6 @@ function getParams() $('#editorcontainer').css({"top":"0px"}); } } - if(showChat) { if(showChat == "false") @@ -110,7 +112,6 @@ function getParams() $('#chaticon').hide(); } } - if(showLineNumbers) { if(showLineNumbers == "false") @@ -118,7 +119,6 @@ function getParams() LineNumbersDisabled = true; } } - if(useMonospaceFont) { if(useMonospaceFont == "true") @@ -126,13 +126,15 @@ 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(); + } } function getUrlVars() @@ -174,7 +176,7 @@ function handshake() function sendClientReady(isReconnect) { var padId = document.location.pathname.substring(document.location.pathname.lastIndexOf("/") + 1); - padId = unescape(padId); // unescape neccesary due to Safari and Opera interpretation of spaces + padId = decodeURIComponent(padId); // unescape neccesary due to Safari and Opera interpretation of spaces if(!isReconnect) document.title = document.title + " | " + padId; diff --git a/static/js/pad_editbar.js b/static/js/pad_editbar.js index 7cea3c1af..97bd7f85b 100644 --- a/static/js/pad_editbar.js +++ b/static/js/pad_editbar.js @@ -206,15 +206,14 @@ var padeditbar = (function() { if ($('#readonlyinput').is(':checked')) { - $('#qrcode').show(); var basePath = document.location.href.substring(0, document.location.href.indexOf("/p/")); var readonlyLink = basePath + "/ro/" + clientVars.readOnlyId; $('#embedinput').val("