From 46d85a12470c109a93f146bf1bee6f1a1057738f Mon Sep 17 00:00:00 2001 From: dsmedia Date: Fri, 23 Sep 2011 13:20:05 -0500 Subject: [PATCH 01/84] added "build-essential" to list of Ubuntu package dependencies for installing. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e075d8212..785a0835e 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ Here is the **[FAQ](https://github.com/Pita/etherpad-lite/wiki/FAQ)** **As root:**
    -
  1. Install the dependencies. We need the gzip, git, curl, libssl develop libraries and python
    apt-get install gzip git-core curl python libssl-dev

  2. +
  3. Install the dependencies. We need the gzip, git, curl, libssl develop libraries and python
    apt-get install gzip git-core curl python libssl-dev build-essential

  4. Install node.js
    1. Download the latest 0.4.x node.js release from http://nodejs.org/#download
    2. From 2d3272e3b6d4b4678c00447d7d8b22b3f4510387 Mon Sep 17 00:00:00 2001 From: Rob Speer Date: Fri, 30 Sep 2011 00:41:46 -0400 Subject: [PATCH 02/84] make globalPads into an ad-hoc Object that can store values with arbitrary names. Fixes issue #160. --- node/db/PadManager.js | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/node/db/PadManager.js b/node/db/PadManager.js index 8af299ccc..4e16c7c45 100644 --- a/node/db/PadManager.js +++ b/node/db/PadManager.js @@ -21,10 +21,20 @@ require("../db/Pad"); var db = require("./DB").db; -/** - * A Array with all known Pads +/** + * An Object containing all known Pads. Provides "get" and "set" functions, + * which should be used instead of indexing with brackets. These prepend a + * colon to the key, to avoid conflicting with built-in Object methods or with + * these functions themselves. + * + * If this is needed in other places, it would be wise to make this a prototype + * that's defined somewhere more sensible. */ -globalPads = []; +globalPads = { + get: function (name) { return this[':'+name]; }, + set: function (name, value) { this[':'+name] = value; }, + remove: function (name) { delete this[':'+name]; } +}; /** * Returns a Pad Object with the callback @@ -65,7 +75,7 @@ exports.getPad = function(id, text, callback) } } - var pad = globalPads[id]; + var pad = globalPads.get(id); //return pad if its already loaded if(pad != null) @@ -86,7 +96,7 @@ exports.getPad = function(id, text, callback) } else { - globalPads[id] = pad; + globalPads.set(id, pad); callback(null, pad); } }); @@ -110,6 +120,6 @@ exports.isValidPadId = function(padId) //removes a pad from the array exports.unloadPad = function(padId) { - if(globalPads[padId]) - delete globalPads[padId]; + if(globalPads.get(padId)) + globalPads.remove(padId); } From 8223f8471477cc3bf86f164095f1097b9893d6ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Fr=C3=B6ssman?= Date: Mon, 17 Oct 2011 23:31:39 +0200 Subject: [PATCH 03/84] Kind of a hack but makes issue #158 go away. There is probably a deeper error somewhere in either log4js or express, I don't have the time to investigate that atm. --- node/server.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/node/server.js b/node/server.js index 2bebe6a24..fbaf08e5e 100644 --- a/node/server.js +++ b/node/server.js @@ -91,7 +91,8 @@ async.waterfall([ var httpLogger = log4js.getLogger("http"); app.configure(function() { - app.use(log4js.connectLogger(httpLogger, { level: log4js.levels.INFO, format: ':status, :method :url'})); + if (!(settings.loglevel === "WARN" || settings.loglevel == "ERROR")) + app.use(log4js.connectLogger(httpLogger, { level: log4js.levels.INFO, format: ':status, :method :url'})); app.use(express.cookieParser()); }); From e0cf6098fb73833f5db85294d95875a62163f116 Mon Sep 17 00:00:00 2001 From: Peter 'Pita' Martischka Date: Wed, 19 Oct 2011 21:46:28 +0200 Subject: [PATCH 04/84] fixing issues with bad import uploads, fixed #186 --- node/handler/ImportHandler.js | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/node/handler/ImportHandler.js b/node/handler/ImportHandler.js index 8936e488a..a1ddf9924 100644 --- a/node/handler/ImportHandler.js +++ b/node/handler/ImportHandler.js @@ -61,10 +61,19 @@ exports.doImport = function(req, res, padId) form.parse(req, function(err, fields, files) { - //save the path of the uploaded file - srcFile = files.file.path; - - callback(err); + //the upload failed, stop at this point + if(err || files.file === undefined) + { + console.warn("Uploading Error: " + err.stack); + callback("uploadFailed"); + } + //everything ok, continue + else + { + //save the path of the uploaded file + srcFile = files.file.path; + callback(); + } }); }, @@ -157,6 +166,13 @@ exports.doImport = function(req, res, padId) } ], function(err) { + //the upload failed, there is nothing we can do, send a 500 + if(err == "uploadFailed") + { + res.send(500); + return; + } + if(err) throw err; //close the connection From 0f559347eee8857ea7ecc36f595d8bc872a7dabc Mon Sep 17 00:00:00 2001 From: Peter 'Pita' Martischka Date: Wed, 19 Oct 2011 21:48:36 +0200 Subject: [PATCH 05/84] Fixing a bug that happens when a socket.io connection closes very early after a CLIENT_READY message --- node/handler/PadMessageHandler.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/node/handler/PadMessageHandler.js b/node/handler/PadMessageHandler.js index f89f4934c..438f0ff36 100644 --- a/node/handler/PadMessageHandler.js +++ b/node/handler/PadMessageHandler.js @@ -809,10 +809,15 @@ function handleClientReady(client, message) //Send the clientVars to the Client client.json.send(clientVars); - //Save the revision and the author id in sessioninfos - sessioninfos[client.id].rev = pad.getHeadRevisionNumber(); - sessioninfos[client.id].author = author; - + //sometimes the client disconnects very early and the session of it is already removed + //thats why we have to check that case + if(sessioninfos[client.id] !== undefined) + { + //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 = { "type": "COLLABROOM", From 2cc7dcb751bf15c0c76fe51fb35801228b11953e Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 26 Oct 2011 20:13:10 +0200 Subject: [PATCH 06/84] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b90f789a7..322fb9139 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "description" : "A Etherpad based on node.js", "homepage" : "https://github.com/Pita/etherpad-lite", "keywords" : ["etherpad", "realtime", "collaborative", "editor"], - "author" : "Peter 'Pita' Martischka ", + "author" : "Peter 'Pita' Martischka - Primary Technology Ltd", "contributors": [ { "name": "John McLear", "name": "Hans Pinckaers", From e4481ea22b911d9818750e74ad30c7983d1a34dc Mon Sep 17 00:00:00 2001 From: Lorenzo Gil Date: Mon, 31 Oct 2011 11:18:44 +0100 Subject: [PATCH 07/84] Allow to get the HTML of the pad with the API --- node/db/API.js | 85 ++++++++++++++++++++++++++++++++++++++ node/handler/APIHandler.js | 1 + node/utils/ExportHtml.js | 2 + 3 files changed, 88 insertions(+) diff --git a/node/db/API.js b/node/db/API.js index 9912b098d..2069ce688 100644 --- a/node/db/API.js +++ b/node/db/API.js @@ -25,6 +25,7 @@ var groupManager = require("./GroupManager"); var authorManager = require("./AuthorManager"); var sessionManager = require("./SessionManager"); var async = require("async"); +var exportHtml = require("../utils/ExportHtml"); /**********************/ /**GROUP FUNCTIONS*****/ @@ -169,6 +170,90 @@ exports.setText = function(padID, text, callback) }); } +/** +getHTML(padID, [rev]) returns the html of a pad + +Example returns: + +{code: 0, message:"ok", data: {text:"Welcome Text"}} +{code: 1, message:"padID does not exist", data: null} +*/ +exports.getHTML = function(padID, rev, callback) +{ + if(typeof rev == "function") + { + callback = rev; + rev = undefined; + } + + if (rev !== undefined && typeof rev != "number") + { + if (!isNaN(parseInt(rev))) + { + rev = parseInt(rev); + } + else + { + callback({stop: "rev is not a number"}); + return; + } + } + + if(rev !== undefined && rev < 0) + { + callback({stop: "rev is a negative number"}); + return; + } + + if(rev !== undefined && !is_int(rev)) + { + callback({stop: "rev is a float value"}); + return; + } + + getPadSafe(padID, true, function(err, pad) + { + if(err) + { + callback(err); + return; + } + + //the client asked for a special revision + if(rev !== undefined) + { + //check if this is a valid revision + if(rev > pad.getHeadRevisionNumber()) + { + callback({stop: "rev is higher than the head revision of the pad"}); + return; + } + + //get the html of this revision + exportHtml.getPadHTML(pad, rev, function(err, html) + { + if(!err) + { + data = {html: html}; + } + callback(err, data); + }); + } + //the client wants the latest text, lets return it to him + else + { + exportHtml.getPadHTML(pad, undefined, function (err, html) + { + if(!err) + { + data = {html: html}; + } + callback(err, data); + }); + } + }); +} + /*****************/ /**PAD FUNCTIONS */ /*****************/ diff --git a/node/handler/APIHandler.js b/node/handler/APIHandler.js index 34d63f18d..57eeeb734 100644 --- a/node/handler/APIHandler.js +++ b/node/handler/APIHandler.js @@ -50,6 +50,7 @@ var functions = { "listSessionsOfAuthor" : ["authorID"], "getText" : ["padID", "rev"], "setText" : ["padID", "text"], + "getHTML" : ["padID", "rev"], "getRevisionsCount" : ["padID"], "deletePad" : ["padID"], "getReadOnlyID" : ["padID"], diff --git a/node/utils/ExportHtml.js b/node/utils/ExportHtml.js index dce156ec8..354757e1e 100644 --- a/node/utils/ExportHtml.js +++ b/node/utils/ExportHtml.js @@ -85,6 +85,8 @@ function getPadHTML(pad, revNum, callback) }); } +exports.getPadHTML = getPadHTML; + function getHTMLFromAtext(pad, atext) { var apool = pad.apool(); From 73711abd97aced9bc6c12d1e013d29d5e4315672 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Fr=C3=B6ssman?= Date: Thu, 3 Nov 2011 07:34:51 +0100 Subject: [PATCH 08/84] Added comments --- node/server.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/node/server.js b/node/server.js index fbaf08e5e..57609ac80 100644 --- a/node/server.js +++ b/node/server.js @@ -91,6 +91,8 @@ async.waterfall([ var httpLogger = log4js.getLogger("http"); app.configure(function() { + // If the log level specified in the config file is WARN or ERROR the application server never starts listening to requests as reported in issue #158. + // Not installing the log4js connect logger when the log level has a higher severity than INFO since it would not log at that level anyway. if (!(settings.loglevel === "WARN" || settings.loglevel == "ERROR")) app.use(log4js.connectLogger(httpLogger, { level: log4js.levels.INFO, format: ':status, :method :url'})); app.use(express.cookieParser()); From 90ba811bb556b9dd9e436961087f14a310ce98bc Mon Sep 17 00:00:00 2001 From: Peter 'Pita' Martischka Date: Fri, 4 Nov 2011 03:15:26 +0100 Subject: [PATCH 09/84] Fixed a bug that creates invalid HTML at Export that lets Abiwords parser crash --- node/utils/ExportHtml.js | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/node/utils/ExportHtml.js b/node/utils/ExportHtml.js index dce156ec8..c02e12992 100644 --- a/node/utils/ExportHtml.js +++ b/node/utils/ExportHtml.js @@ -119,8 +119,10 @@ function getHTMLFromAtext(pad, atext) var taker = Changeset.stringIterator(text); var assem = Changeset.stringAssembler(); + var openTags = []; function emitOpenTag(i) { + openTags.unshift(i); assem.append('<'); assem.append(tags[i]); assem.append('>'); @@ -128,10 +130,27 @@ function getHTMLFromAtext(pad, atext) function emitCloseTag(i) { + openTags.shift(); assem.append(''); } + + function orderdCloseTags(tags2close) + { + for(var i=0;i= 0; i--) { if (propVals[i] === LEAVE) { - emitCloseTag(i); + //emitCloseTag(i); + tags2close.push(i); propVals[i] = false; } else if (propVals[i] === STAY) { - emitCloseTag(i); + //emitCloseTag(i); + tags2close.push(i); } } + + orderdCloseTags(tags2close); + for (var i = 0; i < propVals.length; i++) { if (propVals[i] === ENTER || propVals[i] === STAY) @@ -235,14 +261,18 @@ function getHTMLFromAtext(pad, atext) assem.append(_escapeHTML(s)); } // end iteration over spans in line + + var tags2close = []; for (var i = propVals.length - 1; i >= 0; i--) { if (propVals[i]) { - emitCloseTag(i); + tags2close.push(i); propVals[i] = false; } } + + orderdCloseTags(tags2close); } // end processNextChars if (urls) { From f6165eb2ea04493141d4622860cb141cfcdfd062 Mon Sep 17 00:00:00 2001 From: Peter 'Pita' Martischka Date: Sat, 5 Nov 2011 19:34:01 +0100 Subject: [PATCH 10/84] Fixed another bug that breaked the abiword parser --- node/utils/ExportHtml.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/node/utils/ExportHtml.js b/node/utils/ExportHtml.js index c02e12992..9b7ac9038 100644 --- a/node/utils/ExportHtml.js +++ b/node/utils/ExportHtml.js @@ -257,8 +257,13 @@ function getHTMLFromAtext(pad, atext) { chars--; // exclude newline at end of line, if present } + var s = taker.take(chars); - + + //removes the characters with the code 12. Don't know where they come + //from but they break the abiword parser and are completly useless + s = s.replace(String.fromCharCode(12), ""); + assem.append(_escapeHTML(s)); } // end iteration over spans in line From bf0fe9377d006efe361a985dd55a1e945cd4a283 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 6 Nov 2011 12:06:16 +0000 Subject: [PATCH 11/84] Input fix for Wikinaut --- static/css/pad.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/css/pad.css b/static/css/pad.css index 45385a5b7..3d170c3dd 100644 --- a/static/css/pad.css +++ b/static/css/pad.css @@ -93,7 +93,7 @@ a img } input[type="file"] { - color: #fff; + color: #000; } #editbar ul li.separator From 4fc4a3538148bc995fff989794e91779ed4c847f Mon Sep 17 00:00:00 2001 From: jaseg Date: Wed, 9 Nov 2011 17:19:00 +0100 Subject: [PATCH 12/84] Spelling fixes in log messages, made a delay windows-only which according to a comment should be windows-only, fixed a random filename generator. --- node/handler/ExportHandler.js | 16 +++++++++------- node/handler/PadMessageHandler.js | 22 +++++++++++----------- node/handler/TimesliderMessageHandler.js | 14 +++++++------- 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/node/handler/ExportHandler.js b/node/handler/ExportHandler.js index a87219bb7..8cc74747d 100644 --- a/node/handler/ExportHandler.js +++ b/node/handler/ExportHandler.js @@ -81,10 +81,9 @@ exports.doExport = function(req, res, padId, type) res.send(html); callback("stop"); } - //write the html export to a file - else + else //write the html export to a file { - randNum = Math.floor(Math.random()*new Date().getTime()); + randNum = Math.floor(Math.random()*0xFFFFFFFF); srcFile = tempDirectory + "/eplite_export_" + randNum + ".html"; fs.writeFile(srcFile, html, callback); } @@ -114,10 +113,13 @@ exports.doExport = function(req, res, padId, type) function(callback) { //100ms delay to accomidate for slow windows fs - setTimeout(function() - { - fs.unlink(destFile, callback); - }, 100); + if(os.type().indexOf("Windows") > -1) + { + setTimeout(function() + { + fs.unlink(destFile, callback); + }, 100); + } } ], callback); } diff --git a/node/handler/PadMessageHandler.js b/node/handler/PadMessageHandler.js index 438f0ff36..0fdece233 100644 --- a/node/handler/PadMessageHandler.js +++ b/node/handler/PadMessageHandler.js @@ -193,7 +193,7 @@ exports.handleMessage = function(client, message) //if the message type is unkown, throw an exception else { - messageLogger.warn("Droped message, unkown Message Type " + message.type); + messageLogger.warn("Dropped message, unkown Message Type " + message.type); } } @@ -272,12 +272,12 @@ function handleSuggestUserName(client, message) //check if all ok if(message.data.payload.newName == null) { - messageLogger.warn("Droped message, suggestUserName Message has no newName!"); + messageLogger.warn("Dropped message, suggestUserName Message has no newName!"); return; } if(message.data.payload.unnamedId == null) { - messageLogger.warn("Droped message, suggestUserName Message has no unnamedId!"); + messageLogger.warn("Dropped message, suggestUserName Message has no unnamedId!"); return; } @@ -304,7 +304,7 @@ function handleUserInfoUpdate(client, message) //check if all ok if(message.data.userInfo.colorId == null) { - messageLogger.warn("Droped message, USERINFO_UPDATE Message has no colorId!"); + messageLogger.warn("Dropped message, USERINFO_UPDATE Message has no colorId!"); return; } @@ -348,17 +348,17 @@ function handleUserChanges(client, message) //check if all ok if(message.data.baseRev == null) { - messageLogger.warn("Droped message, USER_CHANGES Message has no baseRev!"); + messageLogger.warn("Dropped message, USER_CHANGES Message has no baseRev!"); return; } if(message.data.apool == null) { - messageLogger.warn("Droped message, USER_CHANGES Message has no apool!"); + messageLogger.warn("Dropped message, USER_CHANGES Message has no apool!"); return; } if(message.data.changeset == null) { - messageLogger.warn("Droped message, USER_CHANGES Message has no changeset!"); + messageLogger.warn("Dropped message, USER_CHANGES Message has no changeset!"); return; } @@ -600,22 +600,22 @@ function handleClientReady(client, message) //check if all ok if(!message.token) { - messageLogger.warn("Droped message, CLIENT_READY Message has no token!"); + messageLogger.warn("Dropped message, CLIENT_READY message has no token!"); return; } if(!message.padId) { - messageLogger.warn("Droped message, CLIENT_READY Message has no padId!"); + messageLogger.warn("Dropped message, CLIENT_READY message has no padId!"); return; } if(!message.protocolVersion) { - messageLogger.warn("Droped message, CLIENT_READY Message has no protocolVersion!"); + messageLogger.warn("Dropped message, CLIENT_READY message has no protocolVersion!"); return; } if(message.protocolVersion != 2) { - messageLogger.warn("Droped message, CLIENT_READY Message has a unkown protocolVersion '" + message.protocolVersion + "'!"); + messageLogger.warn("Dropped message, CLIENT_READY message has an unkown protocolVersion '" + message.protocolVersion + "'!"); return; } diff --git a/node/handler/TimesliderMessageHandler.js b/node/handler/TimesliderMessageHandler.js index a06ab54c8..fa272cc91 100644 --- a/node/handler/TimesliderMessageHandler.js +++ b/node/handler/TimesliderMessageHandler.js @@ -77,7 +77,7 @@ exports.handleMessage = function(client, message) //if the message type is unkown, throw an exception else { - messageLogger.warn("Droped message, unkown Message Type: '" + message.type + "'"); + messageLogger.warn("Dropped message, unkown Message Type: '" + message.type + "'"); } } @@ -85,7 +85,7 @@ function handleClientReady(client, message) { if(message.padId == null) { - messageLogger.warn("Droped message, changeset request has no padId!"); + messageLogger.warn("Dropped message, changeset request has no padId!"); return; } @@ -106,27 +106,27 @@ function handleChangesetRequest(client, message) //check if all ok if(message.data == null) { - messageLogger.warn("Droped message, changeset request has no data!"); + messageLogger.warn("Dropped message, changeset request has no data!"); return; } if(message.padId == null) { - messageLogger.warn("Droped message, changeset request has no padId!"); + messageLogger.warn("Dropped message, changeset request has no padId!"); return; } if(message.data.granularity == null) { - messageLogger.warn("Droped message, changeset request has no granularity!"); + messageLogger.warn("Dropped message, changeset request has no granularity!"); return; } if(message.data.start == null) { - messageLogger.warn("Droped message, changeset request has no start!"); + messageLogger.warn("Dropped message, changeset request has no start!"); return; } if(message.data.requestID == null) { - messageLogger.warn("Droped message, changeset request has no requestID!"); + messageLogger.warn("Dropped message, changeset request has no requestID!"); return; } From b0976a292f70c2c6d344bafd2793aed09027f74b Mon Sep 17 00:00:00 2001 From: jaseg Date: Thu, 10 Nov 2011 11:37:12 +0100 Subject: [PATCH 13/84] Fixed a somewhat quirky random string generator, made another windows-only delay actually windows-only. --- node/handler/ImportHandler.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/node/handler/ImportHandler.js b/node/handler/ImportHandler.js index a1ddf9924..ec520a47c 100644 --- a/node/handler/ImportHandler.js +++ b/node/handler/ImportHandler.js @@ -112,7 +112,7 @@ exports.doImport = function(req, res, padId) //convert file to text function(callback) { - var randNum = Math.floor(Math.random()*new Date().getTime()); + var randNum = Math.floor(Math.random()*0xFFFFFFFF); destFile = tempDirectory + "eplite_import_" + randNum + ".txt"; abiword.convertFile(srcFile, destFile, "txt", callback); }, @@ -136,10 +136,13 @@ exports.doImport = function(req, res, padId) //node on windows has a delay on releasing of the file lock. //We add a 100ms delay to work around this - setTimeout(function() - { - callback(err); - }, 100); + if(os.type().indexOf("Windows") > -1) + { + setTimeout(function() + { + callback(err); + }, 100); + } }); }, From eecb45cbfd230062d11aeb95a68f62889c39bf88 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 12 Nov 2011 18:30:38 +0000 Subject: [PATCH 14/84] Focus on input box onLoad --- static/index.html | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/static/index.html b/static/index.html index e995cdb2f..86b33acc0 100644 --- a/static/index.html +++ b/static/index.html @@ -86,12 +86,13 @@ input[type="submit"]::-moz-focus-inner { border: 0 } @-moz-document url-prefix() { input[type="submit"] { padding: 7px } } +
      New Pad

      or create/open a Pad with the name
      -
      - + +
      @@ -123,4 +124,5 @@ //start the costum js if(typeof costumStart == "function") costumStart(); + \ No newline at end of file From 08e0c91204b2d199d6244e794b3778ced2f54492 Mon Sep 17 00:00:00 2001 From: Peter 'Pita' Martischka Date: Sat, 12 Nov 2011 16:46:10 -0800 Subject: [PATCH 15/84] removed the node v0.4 check, it works now with node v0.6 --- bin/installDeps.sh | 7 ------- 1 file changed, 7 deletions(-) diff --git a/bin/installDeps.sh b/bin/installDeps.sh index cb3676063..3fd6da747 100755 --- a/bin/installDeps.sh +++ b/bin/installDeps.sh @@ -20,13 +20,6 @@ hash node > /dev/null 2>&1 || { exit 1 } -#check node version -NODE_VERSION=$(node --version) -if [ ! $(echo $NODE_VERSION | cut -d "." -f 1-2) = "v0.4" ]; then - echo "You're running a wrong version of node, you're using $NODE_VERSION, we need v0.4.x" >&2 - exit 1 -fi - #Is npm installed? hash npm > /dev/null 2>&1 || { echo "Please install npm ( http://npmjs.org )" >&2 From 08fbc3c3bc324765a1e32dc10227057759450b7c Mon Sep 17 00:00:00 2001 From: Peter 'Pita' Martischka Date: Sat, 12 Nov 2011 16:46:26 -0800 Subject: [PATCH 16/84] Updated all dependencies --- package.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 322fb9139..7be586830 100644 --- a/package.json +++ b/package.json @@ -10,16 +10,16 @@ "name": "Robin Buse"} ], "dependencies" : { - "socket.io" : "0.7.9", + "socket.io" : "0.8.7", "ueberDB" : "0.1.2", - "async" : "0.1.9", - "joose" : "3.18.0", - "express" : "2.4.5", + "async" : "0.1.15", + "joose" : "3.50.0", + "express" : "2.5.0", "clean-css" : "0.2.4", - "uglify-js" : "1.0.7", + "uglify-js" : "1.1.1", "gzip" : "0.1.0", - "formidable" : "1.0.2", - "log4js" : "0.3.8" + "formidable" : "1.0.7", + "log4js" : "0.3.9" }, "version" : "1.0.0" } From 820326dc2bb70c298325fd03145bbd6a5f691f79 Mon Sep 17 00:00:00 2001 From: Peter 'Pita' Martischka Date: Sat, 12 Nov 2011 16:47:01 -0800 Subject: [PATCH 17/84] Output more informations on message dropped --- node/handler/PadMessageHandler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/handler/PadMessageHandler.js b/node/handler/PadMessageHandler.js index 0fdece233..af4aa6426 100644 --- a/node/handler/PadMessageHandler.js +++ b/node/handler/PadMessageHandler.js @@ -193,7 +193,7 @@ exports.handleMessage = function(client, message) //if the message type is unkown, throw an exception else { - messageLogger.warn("Dropped message, unkown Message Type " + message.type); + messageLogger.warn("Dropped message, unkown Message Type " + message.type + ": " + JSON.stringify(message)); } } From 5f12744446792045446d652e2fd4b38faac8db25 Mon Sep 17 00:00:00 2001 From: Peter 'Pita' Martischka Date: Sat, 12 Nov 2011 17:32:30 -0800 Subject: [PATCH 18/84] update ueberDB --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7be586830..f1c30caa7 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ ], "dependencies" : { "socket.io" : "0.8.7", - "ueberDB" : "0.1.2", + "ueberDB" : "0.1.3", "async" : "0.1.15", "joose" : "3.50.0", "express" : "2.5.0", From dac828e8f6213c19247a698bf092c97a82a94d2e Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 13 Nov 2011 14:32:21 +0000 Subject: [PATCH 19/84] Update static/index.html --- static/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/index.html b/static/index.html index 86b33acc0..2bed58f98 100644 --- a/static/index.html +++ b/static/index.html @@ -91,7 +91,7 @@
      New Pad

      or create/open a Pad with the name
      -
      +
      From a5a9592031e2833a18f89556fc0e92b5d00edba2 Mon Sep 17 00:00:00 2001 From: Peter 'Pita' Martischka Date: Sun, 13 Nov 2011 20:25:22 -0800 Subject: [PATCH 20/84] Revert "Fixing a bug that happens when a socket.io connection closes very early after a CLIENT_READY message" This reverts commit 0f559347eee8857ea7ecc36f595d8bc872a7dabc. --- node/handler/PadMessageHandler.js | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/node/handler/PadMessageHandler.js b/node/handler/PadMessageHandler.js index af4aa6426..6e24db6b5 100644 --- a/node/handler/PadMessageHandler.js +++ b/node/handler/PadMessageHandler.js @@ -809,15 +809,10 @@ function handleClientReady(client, message) //Send the clientVars to the Client client.json.send(clientVars); - //sometimes the client disconnects very early and the session of it is already removed - //thats why we have to check that case - if(sessioninfos[client.id] !== undefined) - { - //Save the revision and the author id in sessioninfos - sessioninfos[client.id].rev = pad.getHeadRevisionNumber(); - sessioninfos[client.id].author = author; - } - + //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 = { "type": "COLLABROOM", From 9eac04aaa61d6199ae5833c676447b4eef0c459e Mon Sep 17 00:00:00 2001 From: Peter 'Pita' Martischka Date: Sun, 13 Nov 2011 20:33:13 -0800 Subject: [PATCH 21/84] Fixed a huge problem with the sessioninfos array --- node/handler/PadMessageHandler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/handler/PadMessageHandler.js b/node/handler/PadMessageHandler.js index 6e24db6b5..5dccafc54 100644 --- a/node/handler/PadMessageHandler.js +++ b/node/handler/PadMessageHandler.js @@ -136,7 +136,7 @@ exports.handleDisconnect = function(client) { if(pad2sessions[sessionPad][i] == client.id) { - delete pad2sessions[sessionPad][i]; + pad2sessions[sessionPad].splice(i, 1); break; } } From 646d6a48753b6d4760ada80056a32987b957dae2 Mon Sep 17 00:00:00 2001 From: Peter 'Pita' Martischka Date: Sun, 13 Nov 2011 21:03:05 -0800 Subject: [PATCH 22/84] updated node.js version for buildForWindows Script to 0.6.1 --- bin/buildForWindows.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/buildForWindows.sh b/bin/buildForWindows.sh index 1c693fa55..fc1ac4f9d 100755 --- a/bin/buildForWindows.sh +++ b/bin/buildForWindows.sh @@ -1,6 +1,6 @@ #!/bin/sh -NODE_VERSION="0.5.4" +NODE_VERSION="0.6.1" #Move to the folder where ep-lite is installed cd `dirname $0` From a6f7a63478b3b71ba866e9eeea7b10d103000b46 Mon Sep 17 00:00:00 2001 From: Peter 'Pita' Martischka Date: Sun, 13 Nov 2011 21:18:23 -0800 Subject: [PATCH 23/84] removed unneeded old parts from buildForWindows --- bin/buildForWindows.sh | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/bin/buildForWindows.sh b/bin/buildForWindows.sh index fc1ac4f9d..8e6ce4f6e 100755 --- a/bin/buildForWindows.sh +++ b/bin/buildForWindows.sh @@ -48,16 +48,6 @@ cp -rL node_modules node_modules_resolved rm -rf node_modules mv node_modules_resolved node_modules -echo "remove sqlite, cause we can't use it with windows..." -rm -rf node_modules/ueberDB/node_modules/sqlite3 - -echo "replace log4js with a patched log4js, this log4js runs on windows too..." -rm -rf node_modules/log4js/* -wget https://github.com/Pita/log4js-node/zipball/master -O log4js.zip -unzip log4js.zip -mv Pita-log4js-node*/* node_modules/log4js -rm -rf log4js.zip Pita-log4js-node* - echo "download windows node..." cd bin wget "http://nodejs.org/dist/v$NODE_VERSION/node.exe" -O node.exe From ce4d9df665146045dab8b20abd62e5ca68a6a8aa Mon Sep 17 00:00:00 2001 From: andrehjr Date: Tue, 15 Nov 2011 21:33:43 -0200 Subject: [PATCH 24/84] Fix issue #182: Pad forgets usernames --- static/js/pad2.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/static/js/pad2.js b/static/js/pad2.js index 9df57ae8f..394eb4c71 100644 --- a/static/js/pad2.js +++ b/static/js/pad2.js @@ -81,7 +81,7 @@ function getParams() { var showControls = getUrlVars()["showControls"]; var showChat = getUrlVars()["showChat"]; - var userName = unescape(getUrlVars()["userName"]); + var userName = getUrlVars()["userName"]; var showLineNumbers = getUrlVars()["showLineNumbers"]; var useMonospaceFont = getUrlVars()["useMonospaceFont"]; var IsnoColors = getUrlVars()["noColors"]; @@ -130,7 +130,7 @@ function getParams() 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 = userName; + globalUserName = unescape(userName); } } From 8e7e74170e480a5ceebade3ee3e23347043171ae Mon Sep 17 00:00:00 2001 From: andrehjr Date: Tue, 15 Nov 2011 21:36:26 -0200 Subject: [PATCH 25/84] Extract getUrlVars() function calls to a local var --- static/js/pad2.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/static/js/pad2.js b/static/js/pad2.js index 394eb4c71..d6429eea9 100644 --- a/static/js/pad2.js +++ b/static/js/pad2.js @@ -79,12 +79,13 @@ function randomString() function getParams() { - var showControls = getUrlVars()["showControls"]; - var showChat = getUrlVars()["showChat"]; - var userName = getUrlVars()["userName"]; - var showLineNumbers = getUrlVars()["showLineNumbers"]; - var useMonospaceFont = getUrlVars()["useMonospaceFont"]; - var IsnoColors = getUrlVars()["noColors"]; + var params = getUrlVars() + var showControls = params["showControls"]; + var showChat = params["showChat"]; + var userName = params["userName"]; + var showLineNumbers = params["showLineNumbers"]; + var useMonospaceFont = params["useMonospaceFont"]; + var IsnoColors = params["noColors"]; if(IsnoColors) { From 4e5e5edc8edb2b9d20e6aca115cf86cc7fb17746 Mon Sep 17 00:00:00 2001 From: Lorenzo Gil Sanchez Date: Wed, 16 Nov 2011 17:39:33 +0100 Subject: [PATCH 26/84] Add jsdom as a dependency to be able to parse HTML in the server --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 322fb9139..5632374c0 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,8 @@ "uglify-js" : "1.0.7", "gzip" : "0.1.0", "formidable" : "1.0.2", - "log4js" : "0.3.8" + "log4js" : "0.3.8", + "jsdom" : "0.2.9" }, "version" : "1.0.0" } From fd056cf09c39ae26eb28f1c8d37add5cab917538 Mon Sep 17 00:00:00 2001 From: Lorenzo Gil Sanchez Date: Wed, 16 Nov 2011 19:24:28 +0100 Subject: [PATCH 27/84] Adapt client code for its use in the server --- static/js/ace2_common.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/static/js/ace2_common.js b/static/js/ace2_common.js index 02dc350f7..184785863 100644 --- a/static/js/ace2_common.js +++ b/static/js/ace2_common.js @@ -74,8 +74,12 @@ function isArray(testObject) return testObject && typeof testObject === 'object' && !(testObject.propertyIsEnumerable('length')) && typeof testObject.length === 'number'; } +if (typeof exports !== "undefined") +{ + var navigator = {userAgent: "node-js"}; +} // Figure out what browser is being used (stolen from jquery 1.2.1) -var userAgent = navigator.userAgent.toLowerCase(); +userAgent = navigator.userAgent.toLowerCase(); var browser = { version: (userAgent.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/) || [])[1], safari: /webkit/.test(userAgent), @@ -85,6 +89,7 @@ var browser = { windows: /windows/.test(userAgent) // dgreensp }; + function getAssoc(obj, name) { return obj["_magicdom_" + name]; @@ -130,3 +135,8 @@ function htmlPrettyEscape(str) { return str.replace(/&/g, '&').replace(//g, '>').replace(/\r?\n/g, '\\n'); } + +if (typeof exports !== "undefined") +{ + exports.map = map; +} \ No newline at end of file From 09cb367d8ec49a3da641b378aede7aa2b5781790 Mon Sep 17 00:00:00 2001 From: Lorenzo Gil Sanchez Date: Wed, 16 Nov 2011 19:25:17 +0100 Subject: [PATCH 28/84] The contentcollector code is harder to adapt since it has the Changeset module as a dependency --- node/utils/contentcollector.js | 692 +++++++++++++++++++++++++++++++++ 1 file changed, 692 insertions(+) create mode 100644 node/utils/contentcollector.js diff --git a/node/utils/contentcollector.js b/node/utils/contentcollector.js new file mode 100644 index 000000000..60bd0a6ef --- /dev/null +++ b/node/utils/contentcollector.js @@ -0,0 +1,692 @@ +// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.contentcollector +// %APPJET%: import("etherpad.collab.ace.easysync2.Changeset"); +// %APPJET%: import("etherpad.admin.plugins"); +/** + * 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. + */ + +var Changeset = require("../utils/Changeset"); + +var _MAX_LIST_LEVEL = 8; + +function sanitizeUnicode(s) +{ + return s.replace(/[\uffff\ufffe\ufeff\ufdd0-\ufdef\ud800-\udfff]/g, '?'); +} + +function makeContentCollector(collectStyles, browser, apool, domInterface, className2Author) +{ + browser = browser || {}; + + var plugins_; + if (typeof(plugins) != 'undefined') + { + plugins_ = plugins; + } + else + { + plugins_ = {callHook: function () {}}; + } + + var dom = domInterface || { + isNodeText: function(n) + { + return (n.nodeType == 3); + }, + nodeTagName: function(n) + { + return n.tagName; + }, + nodeValue: function(n) + { + return n.nodeValue; + }, + nodeNumChildren: function(n) + { + return n.childNodes.length; + }, + nodeChild: function(n, i) + { + return n.childNodes.item(i); + }, + nodeProp: function(n, p) + { + return n[p]; + }, + nodeAttr: function(n, a) + { + return n.getAttribute(a); + }, + optNodeInnerHTML: function(n) + { + return n.innerHTML; + } + }; + + var _blockElems = { + "div": 1, + "p": 1, + "pre": 1, + "li": 1 + }; + + function isBlockElement(n) + { + return !!_blockElems[(dom.nodeTagName(n) || "").toLowerCase()]; + } + + function textify(str) + { + return sanitizeUnicode( + str.replace(/[\n\r ]/g, ' ').replace(/\xa0/g, ' ').replace(/\t/g, ' ')); + } + + function getAssoc(node, name) + { + return dom.nodeProp(node, "_magicdom_" + name); + } + + var lines = (function() + { + var textArray = []; + var attribsArray = []; + var attribsBuilder = null; + var op = Changeset.newOp('+'); + var self = { + length: function() + { + return textArray.length; + }, + atColumnZero: function() + { + return textArray[textArray.length - 1] === ""; + }, + startNew: function() + { + textArray.push(""); + self.flush(true); + attribsBuilder = Changeset.smartOpAssembler(); + }, + textOfLine: function(i) + { + return textArray[i]; + }, + appendText: function(txt, attrString) + { + textArray[textArray.length - 1] += txt; + //dmesg(txt+" / "+attrString); + op.attribs = attrString; + op.chars = txt.length; + attribsBuilder.append(op); + }, + textLines: function() + { + return textArray.slice(); + }, + attribLines: function() + { + return attribsArray; + }, + // call flush only when you're done + flush: function(withNewline) + { + if (attribsBuilder) + { + attribsArray.push(attribsBuilder.toString()); + attribsBuilder = null; + } + } + }; + self.startNew(); + return self; + }()); + var cc = {}; + + function _ensureColumnZero(state) + { + if (!lines.atColumnZero()) + { + cc.startNewLine(state); + } + } + var selection, startPoint, endPoint; + var selStart = [-1, -1], + selEnd = [-1, -1]; + var blockElems = { + "div": 1, + "p": 1, + "pre": 1 + }; + + function _isEmpty(node, state) + { + // consider clean blank lines pasted in IE to be empty + if (dom.nodeNumChildren(node) == 0) return true; + if (dom.nodeNumChildren(node) == 1 && getAssoc(node, "shouldBeEmpty") && dom.optNodeInnerHTML(node) == " " && !getAssoc(node, "unpasted")) + { + if (state) + { + var child = dom.nodeChild(node, 0); + _reachPoint(child, 0, state); + _reachPoint(child, 1, state); + } + return true; + } + return false; + } + + function _pointHere(charsAfter, state) + { + var ln = lines.length() - 1; + var chr = lines.textOfLine(ln).length; + if (chr == 0 && state.listType && state.listType != 'none') + { + chr += 1; // listMarker + } + chr += charsAfter; + return [ln, chr]; + } + + function _reachBlockPoint(nd, idx, state) + { + if (!dom.isNodeText(nd)) _reachPoint(nd, idx, state); + } + + function _reachPoint(nd, idx, state) + { + if (startPoint && nd == startPoint.node && startPoint.index == idx) + { + selStart = _pointHere(0, state); + } + if (endPoint && nd == endPoint.node && endPoint.index == idx) + { + selEnd = _pointHere(0, state); + } + } + cc.incrementFlag = function(state, flagName) + { + state.flags[flagName] = (state.flags[flagName] || 0) + 1; + } + cc.decrementFlag = function(state, flagName) + { + state.flags[flagName]--; + } + cc.incrementAttrib = function(state, attribName) + { + if (!state.attribs[attribName]) + { + state.attribs[attribName] = 1; + } + else + { + state.attribs[attribName]++; + } + _recalcAttribString(state); + } + cc.decrementAttrib = function(state, attribName) + { + state.attribs[attribName]--; + _recalcAttribString(state); + } + + function _enterList(state, listType) + { + var oldListType = state.listType; + state.listLevel = (state.listLevel || 0) + 1; + if (listType != 'none') + { + state.listNesting = (state.listNesting || 0) + 1; + } + state.listType = listType; + _recalcAttribString(state); + return oldListType; + } + + function _exitList(state, oldListType) + { + state.listLevel--; + if (state.listType != 'none') + { + state.listNesting--; + } + state.listType = oldListType; + _recalcAttribString(state); + } + + function _enterAuthor(state, author) + { + var oldAuthor = state.author; + state.authorLevel = (state.authorLevel || 0) + 1; + state.author = author; + _recalcAttribString(state); + return oldAuthor; + } + + function _exitAuthor(state, oldAuthor) + { + state.authorLevel--; + state.author = oldAuthor; + _recalcAttribString(state); + } + + function _recalcAttribString(state) + { + var lst = []; + for (var a in state.attribs) + { + if (state.attribs[a]) + { + lst.push([a, 'true']); + } + } + if (state.authorLevel > 0) + { + var authorAttrib = ['author', state.author]; + if (apool.putAttrib(authorAttrib, true) >= 0) + { + // require that author already be in pool + // (don't add authors from other documents, etc.) + lst.push(authorAttrib); + } + } + state.attribString = Changeset.makeAttribsString('+', lst, apool); + } + + function _produceListMarker(state) + { + lines.appendText('*', Changeset.makeAttribsString('+', [ + ['list', state.listType], + ['insertorder', 'first'] + ], apool)); + } + cc.startNewLine = function(state) + { + if (state) + { + var atBeginningOfLine = lines.textOfLine(lines.length() - 1).length == 0; + if (atBeginningOfLine && state.listType && state.listType != 'none') + { + _produceListMarker(state); + } + } + lines.startNew(); + } + cc.notifySelection = function(sel) + { + if (sel) + { + selection = sel; + startPoint = selection.startPoint; + endPoint = selection.endPoint; + } + }; + cc.doAttrib = function(state, na) + { + state.localAttribs = (state.localAttribs || []); + state.localAttribs.push(na); + cc.incrementAttrib(state, na); + }; + cc.collectContent = function(node, state) + { + if (!state) + { + state = { + flags: { /*name -> nesting counter*/ + }, + localAttribs: null, + attribs: { /*name -> nesting counter*/ + }, + attribString: '' + }; + } + var localAttribs = state.localAttribs; + state.localAttribs = null; + var isBlock = isBlockElement(node); + var isEmpty = _isEmpty(node, state); + if (isBlock) _ensureColumnZero(state); + var startLine = lines.length() - 1; + _reachBlockPoint(node, 0, state); + if (dom.isNodeText(node)) + { + var txt = dom.nodeValue(node); + var rest = ''; + var x = 0; // offset into original text + if (txt.length == 0) + { + if (startPoint && node == startPoint.node) + { + selStart = _pointHere(0, state); + } + if (endPoint && node == endPoint.node) + { + selEnd = _pointHere(0, state); + } + } + while (txt.length > 0) + { + var consumed = 0; + if (state.flags.preMode) + { + var firstLine = txt.split('\n', 1)[0]; + consumed = firstLine.length + 1; + rest = txt.substring(consumed); + txt = firstLine; + } + else + { /* will only run this loop body once */ + } + if (startPoint && node == startPoint.node && startPoint.index - x <= txt.length) + { + selStart = _pointHere(startPoint.index - x, state); + } + if (endPoint && node == endPoint.node && endPoint.index - x <= txt.length) + { + selEnd = _pointHere(endPoint.index - x, state); + } + var txt2 = txt; + if ((!state.flags.preMode) && /^[\r\n]*$/.exec(txt)) + { + // prevents textnodes containing just "\n" from being significant + // in safari when pasting text, now that we convert them to + // spaces instead of removing them, because in other cases + // removing "\n" from pasted HTML will collapse words together. + txt2 = ""; + } + var atBeginningOfLine = lines.textOfLine(lines.length() - 1).length == 0; + if (atBeginningOfLine) + { + // newlines in the source mustn't become spaces at beginning of line box + txt2 = txt2.replace(/^\n*/, ''); + } + if (atBeginningOfLine && state.listType && state.listType != 'none') + { + _produceListMarker(state); + } + lines.appendText(textify(txt2), state.attribString); + x += consumed; + txt = rest; + if (txt.length > 0) + { + cc.startNewLine(state); + } + } + } + else + { + var tname = (dom.nodeTagName(node) || "").toLowerCase(); + if (tname == "br") + { + cc.startNewLine(state); + } + else if (tname == "script" || tname == "style") + { + // ignore + } + else if (!isEmpty) + { + var styl = dom.nodeAttr(node, "style"); + var cls = dom.nodeProp(node, "className"); + + var isPre = (tname == "pre"); + if ((!isPre) && browser.safari) + { + isPre = (styl && /\bwhite-space:\s*pre\b/i.exec(styl)); + } + if (isPre) cc.incrementFlag(state, 'preMode'); + var oldListTypeOrNull = null; + var oldAuthorOrNull = null; + if (collectStyles) + { + plugins_.callHook('collectContentPre', { + cc: cc, + state: state, + tname: tname, + styl: styl, + cls: cls + }); + if (tname == "b" || (styl && /\bfont-weight:\s*bold\b/i.exec(styl)) || tname == "strong") + { + cc.doAttrib(state, "bold"); + } + if (tname == "i" || (styl && /\bfont-style:\s*italic\b/i.exec(styl)) || tname == "em") + { + cc.doAttrib(state, "italic"); + } + if (tname == "u" || (styl && /\btext-decoration:\s*underline\b/i.exec(styl)) || tname == "ins") + { + cc.doAttrib(state, "underline"); + } + if (tname == "s" || (styl && /\btext-decoration:\s*line-through\b/i.exec(styl)) || tname == "del") + { + cc.doAttrib(state, "strikethrough"); + } + if (tname == "ul") + { + var type; + var rr = cls && /(?:^| )list-(bullet[12345678])\b/.exec(cls); + type = rr && rr[1] || "bullet" + String(Math.min(_MAX_LIST_LEVEL, (state.listNesting || 0) + 1)); + oldListTypeOrNull = (_enterList(state, type) || 'none'); + } + else if ((tname == "div" || tname == "p") && cls && cls.match(/(?:^| )ace-line\b/)) + { + oldListTypeOrNull = (_enterList(state, type) || 'none'); + } + if (className2Author && cls) + { + var classes = cls.match(/\S+/g); + if (classes && classes.length > 0) + { + for (var i = 0; i < classes.length; i++) + { + var c = classes[i]; + var a = className2Author(c); + if (a) + { + oldAuthorOrNull = (_enterAuthor(state, a) || 'none'); + break; + } + } + } + } + } + + var nc = dom.nodeNumChildren(node); + for (var i = 0; i < nc; i++) + { + var c = dom.nodeChild(node, i); + cc.collectContent(c, state); + } + + if (collectStyles) + { + plugins_.callHook('collectContentPost', { + cc: cc, + state: state, + tname: tname, + styl: styl, + cls: cls + }); + } + + if (isPre) cc.decrementFlag(state, 'preMode'); + if (state.localAttribs) + { + for (var i = 0; i < state.localAttribs.length; i++) + { + cc.decrementAttrib(state, state.localAttribs[i]); + } + } + if (oldListTypeOrNull) + { + _exitList(state, oldListTypeOrNull); + } + if (oldAuthorOrNull) + { + _exitAuthor(state, oldAuthorOrNull); + } + } + } + if (!browser.msie) + { + _reachBlockPoint(node, 1, state); + } + if (isBlock) + { + if (lines.length() - 1 == startLine) + { + cc.startNewLine(state); + } + else + { + _ensureColumnZero(state); + } + } + + if (browser.msie) + { + // in IE, a point immediately after a DIV appears on the next line + _reachBlockPoint(node, 1, state); + } + + state.localAttribs = localAttribs; + }; + // can pass a falsy value for end of doc + cc.notifyNextNode = function(node) + { + // an "empty block" won't end a line; this addresses an issue in IE with + // typing into a blank line at the end of the document. typed text + // goes into the body, and the empty line div still looks clean. + // it is incorporated as dirty by the rule that a dirty region has + // to end a line. + if ((!node) || (isBlockElement(node) && !_isEmpty(node))) + { + _ensureColumnZero(null); + } + }; + // each returns [line, char] or [-1,-1] + var getSelectionStart = function() + { + return selStart; + }; + var getSelectionEnd = function() + { + return selEnd; + }; + + // returns array of strings for lines found, last entry will be "" if + // last line is complete (i.e. if a following span should be on a new line). + // can be called at any point + cc.getLines = function() + { + return lines.textLines(); + }; + + cc.finish = function() + { + lines.flush(); + var lineAttribs = lines.attribLines(); + var lineStrings = cc.getLines(); + + lineStrings.length--; + lineAttribs.length--; + + var ss = getSelectionStart(); + var se = getSelectionEnd(); + + function fixLongLines() + { + // design mode does not deal with with really long lines! + var lineLimit = 2000; // chars + var buffer = 10; // chars allowed over before wrapping + var linesWrapped = 0; + var numLinesAfter = 0; + for (var i = lineStrings.length - 1; i >= 0; i--) + { + var oldString = lineStrings[i]; + var oldAttribString = lineAttribs[i]; + if (oldString.length > lineLimit + buffer) + { + var newStrings = []; + var newAttribStrings = []; + while (oldString.length > lineLimit) + { + //var semiloc = oldString.lastIndexOf(';', lineLimit-1); + //var lengthToTake = (semiloc >= 0 ? (semiloc+1) : lineLimit); + lengthToTake = lineLimit; + newStrings.push(oldString.substring(0, lengthToTake)); + oldString = oldString.substring(lengthToTake); + newAttribStrings.push(Changeset.subattribution(oldAttribString, 0, lengthToTake)); + oldAttribString = Changeset.subattribution(oldAttribString, lengthToTake); + } + if (oldString.length > 0) + { + newStrings.push(oldString); + newAttribStrings.push(oldAttribString); + } + + function fixLineNumber(lineChar) + { + if (lineChar[0] < 0) return; + var n = lineChar[0]; + var c = lineChar[1]; + if (n > i) + { + n += (newStrings.length - 1); + } + else if (n == i) + { + var a = 0; + while (c > newStrings[a].length) + { + c -= newStrings[a].length; + a++; + } + n += a; + } + lineChar[0] = n; + lineChar[1] = c; + } + fixLineNumber(ss); + fixLineNumber(se); + linesWrapped++; + numLinesAfter += newStrings.length; + + newStrings.unshift(i, 1); + lineStrings.splice.apply(lineStrings, newStrings); + newAttribStrings.unshift(i, 1); + lineAttribs.splice.apply(lineAttribs, newAttribStrings); + } + } + return { + linesWrapped: linesWrapped, + numLinesAfter: numLinesAfter + }; + } + var wrapData = fixLongLines(); + + return { + selStart: ss, + selEnd: se, + linesWrapped: wrapData.linesWrapped, + numLinesAfter: wrapData.numLinesAfter, + lines: lineStrings, + lineAttribs: lineAttribs + }; + } + + return cc; +} + +exports.makeContentCollector = makeContentCollector; From 9fe6c11d8755fa9e0b625ff840686c7154ffcb35 Mon Sep 17 00:00:00 2001 From: Lorenzo Gil Sanchez Date: Thu, 17 Nov 2011 10:17:52 +0100 Subject: [PATCH 29/84] Main implementation of the setHTML API --- node/utils/ImportHtml.js | 92 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 node/utils/ImportHtml.js diff --git a/node/utils/ImportHtml.js b/node/utils/ImportHtml.js new file mode 100644 index 000000000..0e236e9b0 --- /dev/null +++ b/node/utils/ImportHtml.js @@ -0,0 +1,92 @@ +/** + * Copyright Yaco Sistemas S.L. 2011. + * + * 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 jsdom = require('jsdom').jsdom; +var log4js = require('log4js'); + +var Changeset = require("./Changeset"); +var contentcollector = require("./contentcollector"); +var map = require("../../static/js/ace2_common.js").map; + +function setPadHTML(pad, html, callback) +{ + var apiLogger = log4js.getLogger("ImportHtml"); + + // Clean the pad. This makes the rest of the code easier + // by several orders of magnitude. + pad.setText(""); + var padText = pad.text(); + + // Parse the incoming HTML with jsdom + var doc = jsdom(html); + apiLogger.debug('html:'); + apiLogger.debug(html); + + // Convert a dom tree into a list of lines and attribute liens + // using the content collector object + var cc = contentcollector.makeContentCollector(true, null, pad.pool); + cc.collectContent(doc.childNodes[0]); + var result = cc.finish(); + apiLogger.debug('Lines:'); + var i; + for (i = 0; i < result.lines.length; i += 1) + { + apiLogger.debug('Line ' + (i + 1) + ' text: ' + result.lines[i]); + apiLogger.debug('Line ' + (i + 1) + ' attributes: ' + result.lineAttribs[i]); + } + + // Get the new plain text and its attributes + var newText = map(result.lines, function (e) { + return e + '\n'; + }).join(''); + apiLogger.debug('newText:'); + apiLogger.debug(newText); + var newAttribs = result.lineAttribs.join('|1+1') + '|1+1'; + + function eachAttribRun(attribs, func /*(startInNewText, endInNewText, attribs)*/ ) + { + var attribsIter = Changeset.opIterator(attribs); + var textIndex = 0; + var newTextStart = 0; + var newTextEnd = newText.length - 1; + while (attribsIter.hasNext()) + { + var op = attribsIter.next(); + var nextIndex = textIndex + op.chars; + if (!(nextIndex <= newTextStart || textIndex >= newTextEnd)) + { + func(Math.max(newTextStart, textIndex), Math.min(newTextEnd, nextIndex), op.attribs); + } + textIndex = nextIndex; + } + } + + // create a new changeset with a helper builder object + var builder = Changeset.builder(1); + + // assemble each line into the builder + eachAttribRun(newAttribs, function(start, end, attribs) + { + builder.insert(newText.substring(start, end), attribs); + }); + + // the changeset is ready! + var theChangeset = builder.toString(); + apiLogger.debug('The changeset: ' + theChangeset); + pad.appendRevision(theChangeset); +} + +exports.setPadHTML = setPadHTML; From a26918c69e2a8130333c395334eb45ba262e4aac Mon Sep 17 00:00:00 2001 From: Lorenzo Gil Sanchez Date: Thu, 17 Nov 2011 10:18:35 +0100 Subject: [PATCH 30/84] Hook the ImportHtml module into the API --- node/db/API.js | 22 ++++++++++++++++++++++ node/handler/APIHandler.js | 1 + 2 files changed, 23 insertions(+) diff --git a/node/db/API.js b/node/db/API.js index 2069ce688..c40c49cac 100644 --- a/node/db/API.js +++ b/node/db/API.js @@ -26,6 +26,8 @@ var authorManager = require("./AuthorManager"); var sessionManager = require("./SessionManager"); var async = require("async"); var exportHtml = require("../utils/ExportHtml"); +var importHtml = require("../utils/ImportHtml"); +var cleanText = require("./Pad").cleanText; /**********************/ /**GROUP FUNCTIONS*****/ @@ -254,6 +256,26 @@ exports.getHTML = function(padID, rev, callback) }); } +exports.setHTML = function(padID, html, callback) +{ + //get the pad + getPadSafe(padID, true, function(err, pad) + { + if(err) + { + callback(err); + return; + } + + // add a new changeset with the new html to the pad + importHtml.setPadHTML(pad, cleanText(html)); + + //update the clients on the pad + padMessageHandler.updatePadClients(pad, callback); + + }); +} + /*****************/ /**PAD FUNCTIONS */ /*****************/ diff --git a/node/handler/APIHandler.js b/node/handler/APIHandler.js index 57eeeb734..3e1bcdc51 100644 --- a/node/handler/APIHandler.js +++ b/node/handler/APIHandler.js @@ -51,6 +51,7 @@ var functions = { "getText" : ["padID", "rev"], "setText" : ["padID", "text"], "getHTML" : ["padID", "rev"], + "setHTML" : ["padID", "html"], "getRevisionsCount" : ["padID"], "deletePad" : ["padID"], "getReadOnlyID" : ["padID"], From 73629921919d1775d9a0d8569e64504bce0d3e5f Mon Sep 17 00:00:00 2001 From: John Soros Date: Fri, 18 Nov 2011 03:49:00 +0100 Subject: [PATCH 31/84] Update README.md and settings.json.template: use mysql or sqlite for production --- README.md | 2 ++ settings.json.template | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 785a0835e..e73b5e481 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,8 @@ Here is the **[FAQ](https://github.com/Pita/etherpad-lite/wiki/FAQ)** ## Next Steps You can modify the settings in the file `settings.json` +You should use a dedicated database such as "mysql" if you are planning on using etherpad-lite in a production environment, the "dirty" database driver is only for testing and/or development purposes. + You can update to the latest version with `git pull origin`. The next start with bin/run.sh will update the dependencies Look at this wiki pages: diff --git a/settings.json.template b/settings.json.template index a7afaecc9..199ac6d0b 100644 --- a/settings.json.template +++ b/settings.json.template @@ -8,7 +8,8 @@ "ip": "0.0.0.0", "port" : 9001, - //The Type of the database. You can choose between sqlite and mysql + //The Type of the database. You can choose between dirty, sqlite and mysql + //You should use mysql or sqlite for anything else than testing or development "dbType" : "dirty", //the database specific settings "dbSettings" : { From 38742d12b9f9902fd53e044551b66882776ccd8b Mon Sep 17 00:00:00 2001 From: Peter 'Pita' Martischka Date: Fri, 18 Nov 2011 13:56:26 -0800 Subject: [PATCH 32/84] Improved the README --- README.md | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 785a0835e..030547812 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,14 @@ documented codebase makes it easier for developers to improve the code and contr Etherpad Lite is optimized to be easy embeddable. It provides a [HTTP API](https://github.com/Pita/etherpad-lite/wiki/HTTP-API) that allows your web application to manage pads, users and groups. -You can use this [PHP Client](https://github.com/TomNomNom/etherpad-lite-client) to work with the API -(If you don't want to use PHP, feel free to create a client for your favourite web development language). +There are several clients in for this API: + +* [PHP](https://github.com/TomNomNom/etherpad-lite-client), thx to [TomNomNom](https://github.com/TomNomNom) +* [.Net](https://github.com/ja-jo/EtherpadLiteDotNet), thx to [ja-jo](https://github.com/ja-jo) +* [Node.js](https://github.com/tomassedovic/etherpad-lite-client-js), thx to [tomassedovic](https://github.com/tomassedovic) +* [Ruby](https://github.com/jhollinger/ruby-etherpad-lite), thx to [jhollinger](https://github.com/jhollinger) +* [Python](https://github.com/devjones/PyEtherpadLite), thx to [devjones](https://github.com/devjones) + There is also a [jQuery plugin](https://github.com/johnyma22/etherpad-lite-jquery-plugin) that helps you to embed Pads into your website **Online demo**
      @@ -51,12 +57,14 @@ Here is the **[FAQ](https://github.com/Pita/etherpad-lite/wiki/FAQ)** **As root:**
        -
      1. Install the dependencies. We need the gzip, git, curl, libssl develop libraries and python
        apt-get install gzip git-core curl python libssl-dev build-essential

      2. +
      3. Install the dependencies. We need gzip, git, curl, libssl develop libraries, python and gcc.
        For Debian/Ubuntu apt-get install gzip git-core curl python libssl-dev build-essential
        + For Fedora/CentOS yum install gzip git-core curl python openssl-dev && yum groupinstall "Development Tools" +

      4. Install node.js
          -
        1. Download the latest 0.4.x node.js release from http://nodejs.org/#download
        2. -
        3. Extract it with tar xf node-v0.4*
        4. -
        5. Move into the node folder cd node-v0.4* and build node with ./configure && make && make install
        6. +
        7. Download the latest 0.6.x node.js release from http://nodejs.org/#download
        8. +
        9. Extract it with tar xf node-v0.6*
        10. +
        11. Move into the node folder cd node-v0.6* and build node with ./configure && make && make install
      5. Install npm curl http://npmjs.org/install.sh | sh
      6. From 411984787a542543d377cf82973c5bd3bb62d95d Mon Sep 17 00:00:00 2001 From: Jordan Date: Fri, 18 Nov 2011 21:52:12 -0500 Subject: [PATCH 33/84] Implement POST requests for HTTP API --- node/server.js | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/node/server.js b/node/server.js index 57609ac80..d39fdcae8 100644 --- a/node/server.js +++ b/node/server.js @@ -290,14 +290,13 @@ async.waterfall([ }); var apiLogger = log4js.getLogger("API"); - - //This is a api call, collect all post informations and pass it to the apiHandler - app.get('/api/1/:func', function(req, res) - { + + //This is for making an api call, collecting all post information and passing it to the apiHandler + var apiCaller = function(req, res, fields) { res.header("Server", serverName); res.header("Content-Type", "application/json; charset=utf-8"); - apiLogger.info("REQUEST, " + req.params.func + ", " + JSON.stringify(req.query)); + apiLogger.info("REQUEST, " + req.params.func + ", " + JSON.stringify(fields)); //wrap the send function so we can log the response res._send = res.send; @@ -314,7 +313,22 @@ async.waterfall([ } //call the api handler - apiHandler.handle(req.params.func, req.query, req, res); + apiHandler.handle(req.params.func, fields, req, res); + } + + //This is a api GET call, collect all post informations and pass it to the apiHandler + app.get('/api/1/:func', function(req, res) + { + apiCaller(req, res, req.query) + }); + + //This is a api POST call, collect all post informations and pass it to the apiHandler + app.post('/api/1/:func', function(req, res) + { + new formidable.IncomingForm().parse(req, function(err, fields, files) + { + apiCaller(req, res, fields) + }); }); //The Etherpad client side sends information about how a disconnect happen From d5e303ec9f6c9abae5a57e01d59670b222d05323 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 19 Nov 2011 19:19:33 +0000 Subject: [PATCH 34/84] Follow Link in Pad - https://github.com/Pita/etherpad-lite/issues/143 -- Note that this only supports links beginning in www. We also support https:// etc. so we should encourage users to properly urls. For example: beta.etherpad.org would not be detected by this. --- static/js/linestylefilter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/linestylefilter.js b/static/js/linestylefilter.js index 76f115f4a..5015e5ba0 100644 --- a/static/js/linestylefilter.js +++ b/static/js/linestylefilter.js @@ -237,7 +237,7 @@ linestylefilter.getRegexpFilter = function(regExp, tag) linestylefilter.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]/; linestylefilter.REGEX_URLCHAR = new RegExp('(' + /[-:@a-zA-Z0-9_.,~%+\/\\?=&#;()$]/.source + '|' + linestylefilter.REGEX_WORDCHAR.source + ')'); -linestylefilter.REGEX_URL = new RegExp(/(?:(?:https?|s?ftp|ftps|file|smb|afp|nfs|(x-)?man|gopher|txmt):\/\/|mailto:)/.source + linestylefilter.REGEX_URLCHAR.source + '*(?![:.,;])' + linestylefilter.REGEX_URLCHAR.source, 'g'); +linestylefilter.REGEX_URL = new RegExp(/(?:(?:https?|s?ftp|ftps|file|smb|afp|nfs|(x-)?man|gopher|txmt):\/\/|mailto:|www.)/.source + linestylefilter.REGEX_URLCHAR.source + '*(?![:.,;])' + linestylefilter.REGEX_URLCHAR.source, 'g'); linestylefilter.getURLFilter = linestylefilter.getRegexpFilter( linestylefilter.REGEX_URL, 'url'); From 0aa56136fe548a24957efc819740f4c6d317ea8b Mon Sep 17 00:00:00 2001 From: Joe Corneli Date: Sat, 19 Nov 2011 19:21:23 +0000 Subject: [PATCH 35/84] Fix message typos --- node/handler/PadMessageHandler.js | 14 +++++++------- node/handler/SocketIORouter.js | 2 +- node/handler/TimesliderMessageHandler.js | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/node/handler/PadMessageHandler.js b/node/handler/PadMessageHandler.js index 5dccafc54..79b80483b 100644 --- a/node/handler/PadMessageHandler.js +++ b/node/handler/PadMessageHandler.js @@ -136,7 +136,7 @@ exports.handleDisconnect = function(client) { if(pad2sessions[sessionPad][i] == client.id) { - pad2sessions[sessionPad].splice(i, 1); + delete pad2sessions[sessionPad][i]; break; } } @@ -190,10 +190,10 @@ exports.handleMessage = function(client, message) { handleSuggestUserName(client, message); } - //if the message type is unkown, throw an exception + //if the message type is unknown, throw an exception else { - messageLogger.warn("Dropped message, unkown Message Type " + message.type + ": " + JSON.stringify(message)); + messageLogger.warn("Dropped message, unknown Message Type " + message.type); } } @@ -600,22 +600,22 @@ function handleClientReady(client, message) //check if all ok if(!message.token) { - messageLogger.warn("Dropped message, CLIENT_READY message has no token!"); + messageLogger.warn("Dropped message, CLIENT_READY Message has no token!"); return; } if(!message.padId) { - messageLogger.warn("Dropped message, CLIENT_READY message has no padId!"); + messageLogger.warn("Dropped message, CLIENT_READY Message has no padId!"); return; } if(!message.protocolVersion) { - messageLogger.warn("Dropped message, CLIENT_READY message has no protocolVersion!"); + messageLogger.warn("Dropped message, CLIENT_READY Message has no protocolVersion!"); return; } if(message.protocolVersion != 2) { - messageLogger.warn("Dropped message, CLIENT_READY message has an unkown protocolVersion '" + message.protocolVersion + "'!"); + messageLogger.warn("Dropped message, CLIENT_READY Message has a unknown protocolVersion '" + message.protocolVersion + "'!"); return; } diff --git a/node/handler/SocketIORouter.js b/node/handler/SocketIORouter.js index 2d8f4f44d..00e3e4f3f 100644 --- a/node/handler/SocketIORouter.js +++ b/node/handler/SocketIORouter.js @@ -128,7 +128,7 @@ exports.setSocketIO = function(_socket) //drop message else { - messageLogger.warn("Droped message cause of bad permissions:" + stringifyWithoutPassword(message)); + messageLogger.warn("Dropped message cause of bad permissions:" + stringifyWithoutPassword(message)); } } }); diff --git a/node/handler/TimesliderMessageHandler.js b/node/handler/TimesliderMessageHandler.js index fa272cc91..86864977f 100644 --- a/node/handler/TimesliderMessageHandler.js +++ b/node/handler/TimesliderMessageHandler.js @@ -77,7 +77,7 @@ exports.handleMessage = function(client, message) //if the message type is unkown, throw an exception else { - messageLogger.warn("Dropped message, unkown Message Type: '" + message.type + "'"); + messageLogger.warn("Dropped message, unknown Message Type: '" + message.type + "'"); } } From 54c8e46cdf2010b7002def4f4762bfee0646c6be Mon Sep 17 00:00:00 2001 From: Peter 'Pita' Martischka Date: Sat, 19 Nov 2011 12:07:39 -0800 Subject: [PATCH 36/84] Fixed Joes typo commit --- node/handler/PadMessageHandler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/handler/PadMessageHandler.js b/node/handler/PadMessageHandler.js index 79b80483b..5c855651c 100644 --- a/node/handler/PadMessageHandler.js +++ b/node/handler/PadMessageHandler.js @@ -136,7 +136,7 @@ exports.handleDisconnect = function(client) { if(pad2sessions[sessionPad][i] == client.id) { - delete pad2sessions[sessionPad][i]; + pad2sessions[sessionPad].splice(i, 1); break; } } From a6b4260cc9e923c7964e1a8ee74097b0d579dc61 Mon Sep 17 00:00:00 2001 From: Peter 'Pita' Martischka Date: Sat, 19 Nov 2011 12:18:21 -0800 Subject: [PATCH 37/84] Updated JQuery to 1.7, thx to @wikinaut --- bin/installDeps.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/installDeps.sh b/bin/installDeps.sh index 3fd6da747..59e474563 100755 --- a/bin/installDeps.sh +++ b/bin/installDeps.sh @@ -47,9 +47,9 @@ npm install || { echo "Ensure jQuery is downloaded and up to date..." DOWNLOAD_JQUERY="true" -NEEDED_VERSION="1.6.2" +NEEDED_VERSION="1.7" if [ -f "static/js/jquery.min.js" ]; then - VERSION=$(cat static/js/jquery.min.js | head -n 2 | tail -n 1 | grep -o "v[0-9]*\.[0-9]*\.[0-9]*"); + VERSION=$(cat static/js/jquery.min.js | head -n 1 | grep -o "v[0-9].[0-9]"); if [ ${VERSION#v} = $NEEDED_VERSION ]; then DOWNLOAD_JQUERY="false" From 7a7869a60230893e928fd63110d2ccb5ec3ae307 Mon Sep 17 00:00:00 2001 From: Peter 'Pita' Martischka Date: Sat, 19 Nov 2011 12:20:24 -0800 Subject: [PATCH 38/84] Revert "Follow Link in Pad - https://github.com/Pita/etherpad-lite/issues/143 -- Note that this only supports links beginning in www. We also support https:// etc. so we should encourage users to properly urls. For example: beta.etherpad.org would not be detected by this. " This reverts commit d5e303ec9f6c9abae5a57e01d59670b222d05323. --- static/js/linestylefilter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/linestylefilter.js b/static/js/linestylefilter.js index 5015e5ba0..76f115f4a 100644 --- a/static/js/linestylefilter.js +++ b/static/js/linestylefilter.js @@ -237,7 +237,7 @@ linestylefilter.getRegexpFilter = function(regExp, tag) linestylefilter.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]/; linestylefilter.REGEX_URLCHAR = new RegExp('(' + /[-:@a-zA-Z0-9_.,~%+\/\\?=&#;()$]/.source + '|' + linestylefilter.REGEX_WORDCHAR.source + ')'); -linestylefilter.REGEX_URL = new RegExp(/(?:(?:https?|s?ftp|ftps|file|smb|afp|nfs|(x-)?man|gopher|txmt):\/\/|mailto:|www.)/.source + linestylefilter.REGEX_URLCHAR.source + '*(?![:.,;])' + linestylefilter.REGEX_URLCHAR.source, 'g'); +linestylefilter.REGEX_URL = new RegExp(/(?:(?:https?|s?ftp|ftps|file|smb|afp|nfs|(x-)?man|gopher|txmt):\/\/|mailto:)/.source + linestylefilter.REGEX_URLCHAR.source + '*(?![:.,;])' + linestylefilter.REGEX_URLCHAR.source, 'g'); linestylefilter.getURLFilter = linestylefilter.getRegexpFilter( linestylefilter.REGEX_URL, 'url'); From 53bf7dc19dd4217dca6a9b454cdb72256aedcec1 Mon Sep 17 00:00:00 2001 From: Peter 'Pita' Martischka Date: Sat, 19 Nov 2011 12:30:12 -0800 Subject: [PATCH 39/84] This should fix the IE Cursor Issue #171 --- static/js/ace2_inner.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/static/js/ace2_inner.js b/static/js/ace2_inner.js index 1575fc9ad..9c647b6df 100644 --- a/static/js/ace2_inner.js +++ b/static/js/ace2_inner.js @@ -3097,7 +3097,11 @@ function OUTER(gscope) // Such a div is what IE 6 creates naturally when you make a blank line // in a document of divs. However, when copy-and-pasted the div will // contain a space, so we note its emptiness with a property. - lineElem.innerHTML = ""; + if(browser.version < 9) { + lineElem.innerHTML = ""; + } else { + lineElem.innerHTML = "
        "; + } // a primitive-valued property survives copy-and-paste setAssoc(lineElem, "shouldBeEmpty", true); // an object property doesn't From 2e79bd50a3eb6ba6f3cdb5f30ad776f5249e7cc4 Mon Sep 17 00:00:00 2001 From: Peter 'Pita' Martischka Date: Sat, 19 Nov 2011 14:14:31 -0800 Subject: [PATCH 40/84] fixed socket.io logging --- node/server.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/node/server.js b/node/server.js index d39fdcae8..1e0b6283c 100644 --- a/node/server.js +++ b/node/server.js @@ -436,25 +436,25 @@ async.waterfall([ //this is only a workaround to ensure it works with all browers behind a proxy //we should remove this when the new socket.io version is more stable - io.set('transports', ['xhr-polling']); + //io.set('transports', ['xhr-polling']); var socketIOLogger = log4js.getLogger("socket.io"); io.set('logger', { debug: function (str) { - socketIOLogger.debug(str); + socketIOLogger.debug.apply(socketIOLogger, arguments); }, info: function (str) { - socketIOLogger.info(str); + socketIOLogger.info.apply(socketIOLogger, arguments); }, warn: function (str) { - socketIOLogger.warn(str); + socketIOLogger.warn.apply(socketIOLogger, arguments); }, error: function (str) { - socketIOLogger.error(str); + socketIOLogger.error.apply(socketIOLogger, arguments); }, }); From 697201b3def7cb7173dfebac50624dc694e34fd6 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 20 Nov 2011 03:52:19 +0000 Subject: [PATCH 41/84] Addresses https://github.com/Pita/etherpad-lite/issues/173 but needs a lot of testing and a word of warning, at current doing shift tab deletes the previous character as it emulates a backspace. --- static/js/ace2_inner.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/static/js/ace2_inner.js b/static/js/ace2_inner.js index 9c647b6df..f45c61b23 100644 --- a/static/js/ace2_inner.js +++ b/static/js/ace2_inner.js @@ -3580,9 +3580,15 @@ function OUTER(gscope) function doTabKey(shiftDown) { - if (!doIndentOutdent(shiftDown)) + if (shiftDown === true){ + doDeleteKey(); + } + else { - performDocumentReplaceSelection(THE_TAB); + if (!doIndentOutdent(shiftDown)) + { + performDocumentReplaceSelection(THE_TAB); + } } } From 55a2f46ca90391e9446eff874b83e45a0161d252 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 20 Nov 2011 04:24:58 +0000 Subject: [PATCH 42/84] https://github.com/Pita/etherpad-lite/issues/143 - needs to be included with earlier commit that should still be in master if pita didn't remove. This fixes the bug where a url without http:// wouldnt be detected as a url and then a subsequent bug I am the father of where it would detect it as a user, open a new page but fudge up the url and make the page fail to load. --- static/js/domline.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/static/js/domline.js b/static/js/domline.js index 119b83783..7732805fd 100644 --- a/static/js/domline.js +++ b/static/js/domline.js @@ -152,6 +152,10 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument) { if (href) { + if(!~href.indexOf("http")) // if the url doesn't include http or https etc prefix it. + { + href = "http://"+href; + } extraOpenTags = extraOpenTags + ''; extraCloseTags = '' + extraCloseTags; } From 9850ba43ee5a41a3383e6eebc8b9be756e80b9ad Mon Sep 17 00:00:00 2001 From: Jordan Date: Mon, 21 Nov 2011 01:45:37 -0500 Subject: [PATCH 43/84] Implement a 'requireSession' mode, which requires any user to have a valid session --- node/db/SecurityManager.js | 35 ++++++++++++++++++++++++---------- node/server.js | 18 +++++++++++------ node/utils/Settings.js | 6 ++++++ settings.json.template | 3 +++ settings.json.template_windows | 3 +++ 5 files changed, 49 insertions(+), 16 deletions(-) diff --git a/node/db/SecurityManager.js b/node/db/SecurityManager.js index 7ad8f8d25..762931da0 100644 --- a/node/db/SecurityManager.js +++ b/node/db/SecurityManager.js @@ -23,6 +23,7 @@ var async = require("async"); var authorManager = require("./AuthorManager"); var padManager = require("./PadManager"); var sessionManager = require("./SessionManager"); +var settings = require("../utils/Settings") /** * This function controlls the access to a pad, it checks if the user can access a pad. @@ -34,18 +35,32 @@ var sessionManager = require("./SessionManager"); */ exports.checkAccess = function (padID, sessionID, token, password, callback) { - // it's not a group pad, means we can grant access - if(padID.indexOf("$") == -1) + // a valid session is required (api-only mode) + if(settings.requireSession) { - //get author for this token - authorManager.getAuthor4Token(token, function(err, author) + // no sessionID, access is denied + if(!sessionID) { - // grant access, with author of token - callback(err, {accessStatus: "grant", authorID: author}); - }) - - //don't continue - return; + callback(null, {accessStatus: "deny"}); + return; + } + } + // a session is not required, so we'll check if it's a public pad + else + { + // it's not a group pad, means we can grant access + if(padID.indexOf("$") == -1) + { + //get author for this token + authorManager.getAuthor4Token(token, function(err, author) + { + // grant access, with author of token + callback(err, {accessStatus: "grant", authorID: author}); + }) + + //don't continue + return; + } } var groupID = padID.split("$")[0]; diff --git a/node/server.js b/node/server.js index 1e0b6283c..fe3c40da4 100644 --- a/node/server.js +++ b/node/server.js @@ -210,9 +210,12 @@ async.waterfall([ return; } - res.header("Server", serverName); - var filePath = path.normalize(__dirname + "/../static/pad.html"); - res.sendfile(filePath, { maxAge: exports.maxAge }); + hasPadAccess(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 @@ -225,9 +228,12 @@ async.waterfall([ return; } - res.header("Server", serverName); - var filePath = path.normalize(__dirname + "/../static/timeslider.html"); - res.sendfile(filePath, { maxAge: exports.maxAge }); + hasPadAccess(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 diff --git a/node/utils/Settings.js b/node/utils/Settings.js index 1d855a53d..9f23d114c 100644 --- a/node/utils/Settings.js +++ b/node/utils/Settings.js @@ -42,6 +42,12 @@ exports.dbSettings = { "filename" : "../var/dirty.db" }; * The default Text of a new pad */ exports.defaultPadText = "Welcome to Etherpad Lite!\n\nThis pad text is synchronized as you type, so that everyone viewing this page sees the same text. This allows you to collaborate seamlessly on documents!\n\nEtherpad Lite on Github: http:\/\/j.mp/ep-lite\n"; + +/** + * A flag that requires any user to have a valid session (via the api) before accessing a pad + */ +exports.requireSession = false; + /** * A flag that shows if minification is enabled or not */ diff --git a/settings.json.template b/settings.json.template index 199ac6d0b..f2d375c64 100644 --- a/settings.json.template +++ b/settings.json.template @@ -29,6 +29,9 @@ //the default text of a pad "defaultPadText" : "Welcome to Etherpad Lite!\n\nThis pad text is synchronized as you type, so that everyone viewing this page sees the same text. This allows you to collaborate seamlessly on documents!\n\nEtherpad Lite on Github: http:\/\/j.mp/ep-lite\n", + /* Users must have a session to access pads. This effectively locks etherpad down to using only the API. */ + "requireSession" : false, + /* if true, all css & js will be minified before sending to the client. This will improve the loading performance massivly, but makes it impossible to debug the javascript/css */ "minify" : true, diff --git a/settings.json.template_windows b/settings.json.template_windows index 235ec71a3..560e62be9 100644 --- a/settings.json.template_windows +++ b/settings.json.template_windows @@ -28,6 +28,9 @@ //the default text of a pad "defaultPadText" : "Welcome to Etherpad Lite!\n\nThis pad text is synchronized as you type, so that everyone viewing this page sees the same text. This allows you to collaborate seamlessly on documents!\n\nEtherpad Lite on Github: http:\/\/j.mp/ep-lite\n", + /* Users must have a session to access pads. This effectively locks etherpad down to using only the API. */ + "requireSession" : false, + /* if true, all css & js will be minified before sending to the client. This will improve the loading performance massivly, but makes it impossible to debug the javascript/css */ "minify" : false, From 90e9d552f0ccf8b149b4a0150a3291d0c1a7d18f Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Mon, 21 Nov 2011 16:39:54 +0100 Subject: [PATCH 44/84] fix issue with newline in APIKEY.txt when checking api key --- node/handler/APIHandler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/handler/APIHandler.js b/node/handler/APIHandler.js index 57eeeb734..04464b086 100644 --- a/node/handler/APIHandler.js +++ b/node/handler/APIHandler.js @@ -70,7 +70,7 @@ var functions = { exports.handle = function(functionName, fields, req, res) { //check the api key! - if(fields["apikey"] != apikey) + if(fields["apikey"] != apikey.trim()) { res.send({code: 4, message: "no or wrong API Key", data: null}); return; From 5630889fb098d1289986a807f872e034836b066b Mon Sep 17 00:00:00 2001 From: Jordan Date: Mon, 21 Nov 2011 11:54:29 -0500 Subject: [PATCH 45/84] A more precise description of the requireSession setting --- settings.json.template | 2 +- settings.json.template_windows | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/settings.json.template b/settings.json.template index f2d375c64..5c0dffdaa 100644 --- a/settings.json.template +++ b/settings.json.template @@ -29,7 +29,7 @@ //the default text of a pad "defaultPadText" : "Welcome to Etherpad Lite!\n\nThis pad text is synchronized as you type, so that everyone viewing this page sees the same text. This allows you to collaborate seamlessly on documents!\n\nEtherpad Lite on Github: http:\/\/j.mp/ep-lite\n", - /* Users must have a session to access pads. This effectively locks etherpad down to using only the API. */ + /* Users must have a session to access pads. This effectively allows only group pads to be accessed. */ "requireSession" : false, /* if true, all css & js will be minified before sending to the client. This will improve the loading performance massivly, diff --git a/settings.json.template_windows b/settings.json.template_windows index 560e62be9..d6f9ef14f 100644 --- a/settings.json.template_windows +++ b/settings.json.template_windows @@ -28,7 +28,7 @@ //the default text of a pad "defaultPadText" : "Welcome to Etherpad Lite!\n\nThis pad text is synchronized as you type, so that everyone viewing this page sees the same text. This allows you to collaborate seamlessly on documents!\n\nEtherpad Lite on Github: http:\/\/j.mp/ep-lite\n", - /* Users must have a session to access pads. This effectively locks etherpad down to using only the API. */ + /* Users must have a session to access pads. This effectively allows only group pads to be accessed. */ "requireSession" : false, /* if true, all css & js will be minified before sending to the client. This will improve the loading performance massivly, From 5e7c5d5dd3048054e94021f10c284e71896dcb44 Mon Sep 17 00:00:00 2001 From: Jordan Date: Mon, 21 Nov 2011 12:44:33 -0500 Subject: [PATCH 46/84] Add editOnly option --- node/db/SecurityManager.js | 28 ++++++++++++++++++++++++---- node/utils/Settings.js | 5 +++++ settings.json.template | 3 +++ settings.json.template_windows | 3 +++ 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/node/db/SecurityManager.js b/node/db/SecurityManager.js index 762931da0..0f35d8735 100644 --- a/node/db/SecurityManager.js +++ b/node/db/SecurityManager.js @@ -35,6 +35,8 @@ var settings = require("../utils/Settings") */ exports.checkAccess = function (padID, sessionID, token, password, callback) { + var statusObject; + // a valid session is required (api-only mode) if(settings.requireSession) { @@ -54,8 +56,26 @@ exports.checkAccess = function (padID, sessionID, token, password, callback) //get author for this token authorManager.getAuthor4Token(token, function(err, author) { - // grant access, with author of token - callback(err, {accessStatus: "grant", authorID: author}); + // assume user has access + statusObject = {accessStatus: "grant", authorID: author}; + // user can't create pads + if(settings.editOnly) + { + // check if pad exists + padManager.doesPadExists(padID, function(err, exists) + { + // 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); + }); + } + // user may create new pads - no need to check anything + else + { + // grant access, with author of token + callback(err, statusObject); + } }) //don't continue @@ -72,8 +92,6 @@ exports.checkAccess = function (padID, sessionID, token, password, callback) var isPasswordProtected; var passwordStatus = password == null ? "notGiven" : "wrong"; // notGiven, correct, wrong - var statusObject; - async.series([ //get basic informations from the database function(callback) @@ -195,6 +213,8 @@ exports.checkAccess = function (padID, sessionID, token, password, callback) { //--> grant access statusObject = {accessStatus: "grant", authorID: sessionAuthor}; + //--> deny access if user isn't allowed to create the pad + if(settings.editOnly) statusObject.accessStatus = "deny"; } // there is no valid session avaiable AND pad exists else if(!validSession && padExists) diff --git a/node/utils/Settings.js b/node/utils/Settings.js index 9f23d114c..2aef834d6 100644 --- a/node/utils/Settings.js +++ b/node/utils/Settings.js @@ -48,6 +48,11 @@ exports.defaultPadText = "Welcome to Etherpad Lite!\n\nThis pad text is synchron */ exports.requireSession = false; +/** + * A flag that prevents users from creating new pads + */ +exports.editOnly = false; + /** * A flag that shows if minification is enabled or not */ diff --git a/settings.json.template b/settings.json.template index 5c0dffdaa..a453258fa 100644 --- a/settings.json.template +++ b/settings.json.template @@ -31,6 +31,9 @@ /* Users must have a session to access pads. This effectively allows only group pads to be accessed. */ "requireSession" : false, + + /* Users may edit pads but not create new ones. Pad creation is only via the API. This applies both to group pads and regular pads. */ + "editOnly" : true, /* if true, all css & js will be minified before sending to the client. This will improve the loading performance massivly, but makes it impossible to debug the javascript/css */ diff --git a/settings.json.template_windows b/settings.json.template_windows index d6f9ef14f..c3d6be2a8 100644 --- a/settings.json.template_windows +++ b/settings.json.template_windows @@ -30,6 +30,9 @@ /* Users must have a session to access pads. This effectively allows only group pads to be accessed. */ "requireSession" : false, + + /* Users may edit pads but not create new ones. Pad creation is only via the API. This applies both to group pads and regular pads. */ + "editOnly" : true, /* if true, all css & js will be minified before sending to the client. This will improve the loading performance massivly, but makes it impossible to debug the javascript/css */ From 6297e628eb9ece84518994111647e4b0e7177b71 Mon Sep 17 00:00:00 2001 From: Jordan Date: Mon, 21 Nov 2011 15:51:54 -0500 Subject: [PATCH 47/84] Permissions checks on pad and timeslider in server.js are redudant --- node/server.js | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/node/server.js b/node/server.js index fe3c40da4..1e0b6283c 100644 --- a/node/server.js +++ b/node/server.js @@ -210,12 +210,9 @@ async.waterfall([ return; } - hasPadAccess(req, res, function() - { - res.header("Server", serverName); - var filePath = path.normalize(__dirname + "/../static/pad.html"); - res.sendfile(filePath, { maxAge: exports.maxAge }); - }); + 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 @@ -228,12 +225,9 @@ async.waterfall([ return; } - hasPadAccess(req, res, function() - { - res.header("Server", serverName); - var filePath = path.normalize(__dirname + "/../static/timeslider.html"); - res.sendfile(filePath, { maxAge: exports.maxAge }); - }); + 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 From e6de06b1039aae9559edec2a4bc68ea509710a53 Mon Sep 17 00:00:00 2001 From: Lorenzo Gil Sanchez Date: Tue, 22 Nov 2011 10:09:10 +0100 Subject: [PATCH 48/84] Transform multiline HTML into a single line stream to avoid spurious lines in the final result --- node/utils/ImportHtml.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/utils/ImportHtml.js b/node/utils/ImportHtml.js index 0e236e9b0..c0d38fe50 100644 --- a/node/utils/ImportHtml.js +++ b/node/utils/ImportHtml.js @@ -31,7 +31,7 @@ function setPadHTML(pad, html, callback) var padText = pad.text(); // Parse the incoming HTML with jsdom - var doc = jsdom(html); + var doc = jsdom(html.replace(/>\n+<'); apiLogger.debug('html:'); apiLogger.debug(html); From 943f8c4682296c8b5424f1c5cec40a3fbea24195 Mon Sep 17 00:00:00 2001 From: Lorenzo Gil Sanchez Date: Tue, 22 Nov 2011 16:09:27 +0100 Subject: [PATCH 49/84] Stupid syntax error due to last minute refactoring --- node/utils/ImportHtml.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/utils/ImportHtml.js b/node/utils/ImportHtml.js index c0d38fe50..6441708e1 100644 --- a/node/utils/ImportHtml.js +++ b/node/utils/ImportHtml.js @@ -31,7 +31,7 @@ function setPadHTML(pad, html, callback) var padText = pad.text(); // Parse the incoming HTML with jsdom - var doc = jsdom(html.replace(/>\n+<'); + var doc = jsdom(html.replace(/>\n+<')); apiLogger.debug('html:'); apiLogger.debug(html); From 2db81050f1e53605237ea621de26c06898f7741a Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 22 Nov 2011 21:03:50 +0000 Subject: [PATCH 50/84] Fixes "I did more testing and there are a few issues that comes up. If you type anything starting with www then that entire phrase becomes an anchor tag, even if there is a space after the www. So for example in the sentence "Put www at the start of the address" "www at" would become an anchor. The regex need updating to require a dot after the www, which I think would fix the issue, but my regex knowledge is not great." --- static/js/linestylefilter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/linestylefilter.js b/static/js/linestylefilter.js index 76f115f4a..f6cf9f357 100644 --- a/static/js/linestylefilter.js +++ b/static/js/linestylefilter.js @@ -237,7 +237,7 @@ linestylefilter.getRegexpFilter = function(regExp, tag) linestylefilter.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]/; linestylefilter.REGEX_URLCHAR = new RegExp('(' + /[-:@a-zA-Z0-9_.,~%+\/\\?=&#;()$]/.source + '|' + linestylefilter.REGEX_WORDCHAR.source + ')'); -linestylefilter.REGEX_URL = new RegExp(/(?:(?:https?|s?ftp|ftps|file|smb|afp|nfs|(x-)?man|gopher|txmt):\/\/|mailto:)/.source + linestylefilter.REGEX_URLCHAR.source + '*(?![:.,;])' + linestylefilter.REGEX_URLCHAR.source, 'g'); +linestylefilter.REGEX_URL = new RegExp(/(?:(?:https?|s?ftp|ftps|file|smb|afp|nfs|(x-)?man|gopher|txmt):\/\/|mailto:|www\.)/.source + linestylefilter.REGEX_URLCHAR.source + '*(?![:.,;])' + linestylefilter.REGEX_URLCHAR.source, 'g'); linestylefilter.getURLFilter = linestylefilter.getRegexpFilter( linestylefilter.REGEX_URL, 'url'); From dedd60412577b132ea2946f2af0d1db215bf8fd4 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 23 Nov 2011 15:46:34 +0000 Subject: [PATCH 51/84] Add support for embedding read only etc. --- static/js/pad_editbar.js | 9 ++++++++- static/pad.html | 27 ++++++++++++++++----------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/static/js/pad_editbar.js b/static/js/pad_editbar.js index ae1090687..f78e33fa5 100644 --- a/static/js/pad_editbar.js +++ b/static/js/pad_editbar.js @@ -102,8 +102,15 @@ var padeditbar = (function() { var padurl = window.location.href.split("?")[0]; $('#embedinput').val("