diff --git a/node/server.js b/node/server.js
index c0d6ce6a..68b7ab6a 100644
--- a/node/server.js
+++ b/node/server.js
@@ -84,6 +84,33 @@ async.waterfall([
next();
});
+
+ //redirects browser to the pad's sanitized url if needed. otherwise, renders the html
+ app.param('pad', function (req, res, next, padId) {
+ //ensure the padname is valid and the url doesn't end with a /
+ if(!padManager.isValidPadId(padId) || /\/$/.test(req.url))
+ {
+ res.send('Such a padname is forbidden', 404);
+ }
+ else
+ {
+ padManager.sanitizePadId(padId, function(sanitizedPadId) {
+ //the pad id was sanitized, so we redirect to the sanitized version
+ if(sanitizedPadId != padId)
+ {
+ var real_path = req.path.replace(/^\/p\/[^\/]+/, '/p/' + sanitizedPadId);
+ res.header('Location', real_path);
+ res.send('You should be redirected to ' + real_path + '', 302);
+ }
+ //the pad id was fine, so just render it
+ else
+ {
+ next();
+ }
+ });
+ }
+ });
+
//load modules that needs a initalized db
readOnlyManager = require("./db/ReadOnlyManager");
exporthtml = require("./utils/ExportHtml");
@@ -230,94 +257,60 @@ async.waterfall([
});
});
- //redirects browser to the pad's sanitized url if needed. otherwise, renders the html
- function goToPad(req, res, render) {
- //ensure the padname is valid and the url doesn't end with a /
- if(!padManager.isValidPadId(req.params.pad) || /\/$/.test(req.url))
- {
- res.send('Such a padname is forbidden', 404);
- }
- else
- {
- padManager.sanitizePadId(req.params.pad, function(padId) {
- //the pad id was sanitized, so we redirect to the sanitized version
- if(padId != req.params.pad)
- {
- var real_path = req.path.replace(/^\/p\/[^\/]+/, '/p/' + padId);
- res.header('Location', real_path);
- res.send('You should be redirected to ' + real_path + '', 302);
- }
- //the pad id was fine, so just render it
- else
- {
- render();
- }
- });
- }
- }
-
//serve pad.html under /p
app.get('/p/:pad', function(req, res, next)
{
- goToPad(req, res, function() {
- var filePath = path.normalize(__dirname + "/../static/pad.html");
- res.sendfile(filePath, { maxAge: exports.maxAge });
- });
+ var filePath = path.normalize(__dirname + "/../static/pad.html");
+ res.sendfile(filePath, { maxAge: exports.maxAge });
});
//serve timeslider.html under /p/$padname/timeslider
app.get('/p/:pad/timeslider', function(req, res, next)
{
- goToPad(req, res, function() {
- var filePath = path.normalize(__dirname + "/../static/timeslider.html");
- res.sendfile(filePath, { maxAge: exports.maxAge });
- });
+ var filePath = path.normalize(__dirname + "/../static/timeslider.html");
+ res.sendfile(filePath, { maxAge: exports.maxAge });
});
//serve timeslider.html under /p/$padname/timeslider
app.get('/p/:pad/:rev?/export/:type', function(req, res, next)
{
- goToPad(req, res, function() {
- var types = ["pdf", "doc", "txt", "html", "odt", "dokuwiki"];
- //send a 404 if we don't support this filetype
- if(types.indexOf(req.params.type) == -1)
- {
- next();
- return;
- }
-
- //if abiword is disabled, and this is a format we only support with abiword, output a message
- if(settings.abiword == null &&
- ["odt", "pdf", "doc"].indexOf(req.params.type) !== -1)
- {
- res.send("Abiword is not enabled at this Etherpad Lite instance. Set the path to Abiword in settings.json to enable this feature");
- return;
- }
-
- res.header("Access-Control-Allow-Origin", "*");
-
- hasPadAccess(req, res, function()
- {
- exportHandler.doExport(req, res, req.params.pad, req.params.type);
- });
+ var types = ["pdf", "doc", "txt", "html", "odt", "dokuwiki"];
+ //send a 404 if we don't support this filetype
+ if(types.indexOf(req.params.type) == -1)
+ {
+ next();
+ return;
+ }
+
+ //if abiword is disabled, and this is a format we only support with abiword, output a message
+ if(settings.abiword == null &&
+ ["odt", "pdf", "doc"].indexOf(req.params.type) !== -1)
+ {
+ res.send("Abiword is not enabled at this Etherpad Lite instance. Set the path to Abiword in settings.json to enable this feature");
+ return;
+ }
+
+ res.header("Access-Control-Allow-Origin", "*");
+
+ hasPadAccess(req, res, function()
+ {
+ exportHandler.doExport(req, res, req.params.pad, req.params.type);
});
});
//handle import requests
app.post('/p/:pad/import', function(req, res, next)
{
- goToPad(req, res, function() {
- //if abiword is disabled, skip handling this request
- if(settings.abiword == null)
- {
- next();
- return;
- }
-
- hasPadAccess(req, res, function()
- {
- importHandler.doImport(req, res, req.params.pad);
- });
+ //if abiword is disabled, skip handling this request
+ if(settings.abiword == null)
+ {
+ next();
+ return;
+ }
+
+ hasPadAccess(req, res, function()
+ {
+ importHandler.doImport(req, res, req.params.pad);
});
});
diff --git a/node/utils/Minify.js b/node/utils/Minify.js
index ea7834dc..98774d19 100644
--- a/node/utils/Minify.js
+++ b/node/utils/Minify.js
@@ -306,11 +306,6 @@ function tarCode(filesInOrder, files, write) {
write("\n\n\n/*** File: static/js/" + filename + " ***/\n\n\n");
write(isolateJS(files[filename], filename));
}
-
- for(var i = 0, ii = filesInOrder.length; i < filesInOrder.length; i++) {
- var filename = filesInOrder[i];
- write('require(' + JSON.stringify('/' + filename.replace(/^\/+/, '')) + ');\n');
- }
}
// Wrap the following code in a self executing function and assign exports to
diff --git a/node/utils/tar.json b/node/utils/tar.json
index fd285b94..e1e6fb2b 100644
--- a/node/utils/tar.json
+++ b/node/utils/tar.json
@@ -1,6 +1,7 @@
{
"pad.js": [
"jquery.js"
+ , "pad.js"
, "ace2_common.js"
, "pad_utils.js"
, "plugins.js"
@@ -17,7 +18,6 @@
, "pad_impexp.js"
, "pad_savedrevs.js"
, "pad_connectionstatus.js"
- , "pad2.js"
, "jquery-ui.js"
, "chat.js"
, "excanvas.js"
@@ -46,5 +46,6 @@
, "broadcast.js"
, "broadcast_slider.js"
, "broadcast_revisions.js"
+ , "timeslider.js"
]
}
diff --git a/static/js/broadcast.js b/static/js/broadcast.js
index b49b185a..2aa0b5d0 100644
--- a/static/js/broadcast.js
+++ b/static/js/broadcast.js
@@ -20,16 +20,17 @@
* limitations under the License.
*/
-var global = this;
-
var makeCSSManager = require('/cssmanager_client').makeCSSManager;
var domline = require('/domline_client').domline;
var Changeset = require('/easysync2_client').Changeset;
var AttribPool = require('/easysync2_client').AttribPool;
var linestylefilter = require('/linestylefilter_client').linestylefilter;
-function loadBroadcastJS()
+// These parameters were global, now they are injected. A reference to the
+// Timeslider controller would probably be more appropriate.
+function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, BroadcastSlider)
{
+ var changesetLoader = undefined;
// just in case... (todo: this must be somewhere else in the client code.)
// Below Array#map code was direct pasted by AppJet/Etherpad, licence unknown. Possible source: http://www.tutorialspoint.com/javascript/array_map.htm
if (!Array.prototype.map)
@@ -423,7 +424,7 @@ function loadBroadcastJS()
}));
}
- global.changesetLoader = {
+ changesetLoader = {
running: false,
resolved: [],
requestQueue1: [],
@@ -763,6 +764,8 @@ function loadBroadcastJS()
}
receiveAuthorData(clientVars.historicalAuthorData);
+
+ return changesetLoader;
}
exports.loadBroadcastJS = loadBroadcastJS;
diff --git a/static/js/broadcast_revisions.js b/static/js/broadcast_revisions.js
index 364ac3e8..19f3f5ff 100644
--- a/static/js/broadcast_revisions.js
+++ b/static/js/broadcast_revisions.js
@@ -22,7 +22,6 @@
// revision info is a skip list whos entries represent a particular revision
// of the document. These revisions are connected together by various
// changesets, or deltas, between any two revisions.
-var global = this;
function loadBroadcastRevisionsJS()
{
diff --git a/static/js/broadcast_slider.js b/static/js/broadcast_slider.js
index 972190ac..0bdc2cab 100644
--- a/static/js/broadcast_slider.js
+++ b/static/js/broadcast_slider.js
@@ -19,10 +19,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-var global = this;
-function loadBroadcastSliderJS()
+ // These parameters were global, now they are injected. A reference to the
+ // Timeslider controller would probably be more appropriate.
+function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded)
{
+ var BroadcastSlider;
(function()
{ // wrap this code in its own namespace
@@ -203,7 +205,7 @@ function loadBroadcastSliderJS()
}
}
- global.BroadcastSlider = {
+ BroadcastSlider = {
onSlider: onSlider,
getSliderPosition: getSliderPosition,
setSliderPosition: setSliderPosition,
@@ -495,6 +497,8 @@ function loadBroadcastSliderJS()
{
$("#viewlatest").html(loc == BroadcastSlider.getSliderLength() ? "Viewing latest content" : "View latest content");
})
+
+ return BroadcastSlider;
}
exports.loadBroadcastSliderJS = loadBroadcastSliderJS;
diff --git a/static/js/pad2.js b/static/js/pad.js
similarity index 95%
rename from static/js/pad2.js
rename to static/js/pad.js
index 2c7d7e0e..fb297d4a 100644
--- a/static/js/pad2.js
+++ b/static/js/pad.js
@@ -24,13 +24,14 @@
var socket;
-var settings = {};
-settings.LineNumbersDisabled = false;
-settings.noColors = false;
-settings.useMonospaceFontGlobal = false;
-settings.globalUserName = false;
-settings.hideQRCode = false;
-settings.rtlIsTrue = false;
+// These jQuery things should create local references, but for now `require()`
+// assigns to the global `$` and augments it with plugins.
+require('/jquery');
+require('/jquery-ui');
+require('/farbtastic');
+require('/excanvas');
+require('/json2');
+require('/undo-xpopup');
var chat = require('/chat').chat;
var getCollabClient = require('/collab_client').getCollabClient;
@@ -45,19 +46,6 @@ var padsavedrevs = require('/pad_savedrevs').padsavedrevs;
var paduserlist = require('/pad_userlist').paduserlist;
var padutils = require('/pad_utils').padutils;
-$(document).ready(function()
-{
- //start the costum js
- if(typeof costumStart == "function") costumStart();
- getParams();
- handshake();
-});
-
-$(window).unload(function()
-{
- pad.dispose();
-});
-
function createCookie(name, value, days, path)
{
if (days)
@@ -309,7 +297,7 @@ function handshake()
clientVars.collab_client_vars.clientAgent = "Anonymous";
//initalize the pad
- pad.init();
+ pad._afterHandshake();
initalized = true;
// If the LineNumbersDisabled value is set to true then we need to hide the Line Numbers
@@ -421,6 +409,23 @@ var pad = {
},
init: function()
+ {
+ padutils.setupGlobalExceptionHandler();
+
+ $(document).ready(function()
+ {
+ //start the costum js
+ if(typeof costumStart == "function") costumStart();
+ getParams();
+ handshake();
+ });
+
+ $(window).unload(function()
+ {
+ pad.dispose();
+ });
+ },
+ _afterHandshake: function()
{
pad.clientTimeOffset = new Date().getTime() - clientVars.serverTimestamp;
@@ -446,7 +451,7 @@ var pad = {
}
// order of inits is important here:
- padcookie.init(clientVars.cookiePrefsToSet);
+ padcookie.init(clientVars.cookiePrefsToSet, this);
$("#widthprefcheck").click(pad.toggleWidthPref);
// $("#sidebarcheck").click(pad.togglewSidebar);
@@ -473,16 +478,16 @@ var pad = {
initialTitle: clientVars.initialTitle,
initialPassword: clientVars.initialPassword,
guestPolicy: pad.padOptions.guestPolicy
- });
- padimpexp.init();
- padsavedrevs.init(clientVars.initialRevisionList);
+ }, this);
+ padimpexp.init(this);
+ padsavedrevs.init(clientVars.initialRevisionList, this);
- padeditor.init(postAceInit, pad.padOptions.view || {});
+ padeditor.init(postAceInit, pad.padOptions.view || {}, this);
- paduserlist.init(pad.myUserInfo);
+ paduserlist.init(pad.myUserInfo, this);
// padchat.init(clientVars.chatHistory, pad.myUserInfo);
padconnectionstatus.init();
- padmodals.init();
+ padmodals.init(this);
pad.collabClient = getCollabClient(padeditor.ace, clientVars.collab_client_vars, pad.myUserInfo, {
colorPalette: pad.getColorPalette()
@@ -981,6 +986,21 @@ var alertBar = (function()
return self;
}());
+function init() {
+ return pad.init();
+}
+
+var settings = {
+ LineNumbersDisabled: false
+, noColors: false
+, useMonospaceFontGlobal: false
+, globalUserName: false
+, hideQRCode: false
+, rtlIsTrue: false
+};
+
+pad.settings = settings;
+
exports.settings = settings;
exports.createCookie = createCookie;
exports.readCookie = readCookie;
@@ -990,4 +1010,5 @@ exports.getUrlVars = getUrlVars;
exports.savePassword = savePassword;
exports.handshake = handshake;
exports.pad = pad;
+exports.init = init;
exports.alertBar = alertBar;
diff --git a/static/js/pad_cookie.js b/static/js/pad_cookie.js
index 24dc1e3f..1bb5700a 100644
--- a/static/js/pad_cookie.js
+++ b/static/js/pad_cookie.js
@@ -87,9 +87,9 @@ var padcookie = (function()
var pad = undefined;
var self = {
- init: function(prefsToSet)
+ init: function(prefsToSet, _pad)
{
- pad = require('/pad2').pad; // Sidestep circular dependency (should be injected).
+ pad = _pad;
var rawCookie = getRawCookie();
if (rawCookie)
diff --git a/static/js/pad_docbar.js b/static/js/pad_docbar.js
index cf461c93..b83bf3bf 100644
--- a/static/js/pad_docbar.js
+++ b/static/js/pad_docbar.js
@@ -118,9 +118,9 @@ var paddocbar = (function()
var self = {
title: null,
password: null,
- init: function(opts)
+ init: function(opts, _pad)
{
- pad = require('/pad2').pad; // Sidestep circular dependency (should be injected).
+ pad = _pad;
panels = {
impexp: {
diff --git a/static/js/pad_editor.js b/static/js/pad_editor.js
index e7be8175..6d3cbf40 100644
--- a/static/js/pad_editor.js
+++ b/static/js/pad_editor.js
@@ -32,11 +32,11 @@ var padeditor = (function()
ace: null,
// this is accessed directly from other files
viewZoom: 100,
- init: function(readyFunc, initialViewOptions)
+ init: function(readyFunc, initialViewOptions, _pad)
{
Ace2Editor = require('/ace').Ace2Editor;
- pad = require('/pad2').pad; // Sidestep circular dependency (should be injected).
- settings = require('/pad2').settings;
+ pad = _pad;
+ settings = pad.settings;
function aceReady()
{
diff --git a/static/js/pad_impexp.js b/static/js/pad_impexp.js
index aa99541e..0037195f 100644
--- a/static/js/pad_impexp.js
+++ b/static/js/pad_impexp.js
@@ -236,13 +236,9 @@ var padimpexp = (function()
/////
var pad = undefined;
var self = {
- init: function()
+ init: function(_pad)
{
- try {
- pad = require('/pad2').pad; // Sidestep circular dependency (should be injected).
- } catch (e) {
- // skip (doesn't require pad when required by timeslider)
- }
+ pad = _pad;
//get /p/padname
var pad_root_path = new RegExp(/.*\/p\/[^\/]+/).exec(document.location.pathname)
diff --git a/static/js/pad_modals.js b/static/js/pad_modals.js
index 81ef0776..b78e28f4 100644
--- a/static/js/pad_modals.js
+++ b/static/js/pad_modals.js
@@ -75,9 +75,9 @@ var padmodals = (function()
var pad = undefined;
var self = {
- init: function()
+ init: function(_pad)
{
- pad = require('/pad2').pad; // Sidestep circular dependency (should be injected).
+ pad = _pad;
self.initFeedback();
self.initShareBox();
diff --git a/static/js/pad_savedrevs.js b/static/js/pad_savedrevs.js
index bb52658b..6d37dfa8 100644
--- a/static/js/pad_savedrevs.js
+++ b/static/js/pad_savedrevs.js
@@ -349,9 +349,9 @@ var padsavedrevs = (function()
var pad = undefined;
var self = {
- init: function(initialRevisions)
+ init: function(initialRevisions, _pad)
{
- pad = require('/pad2').pad; // Sidestep circular dependency (should be injected).
+ pad = _pad;
self.newRevisionList(initialRevisions, true);
$("#savedrevs-savenow").click(function()
diff --git a/static/js/pad_userlist.js b/static/js/pad_userlist.js
index e0a12f83..2c063d74 100644
--- a/static/js/pad_userlist.js
+++ b/static/js/pad_userlist.js
@@ -464,9 +464,9 @@ var paduserlist = (function()
var pad = undefined;
var self = {
- init: function(myInitialUserInfo)
+ init: function(myInitialUserInfo, _pad)
{
- pad = require('/pad2').pad; // Sidestep circular dependency (should be injected).
+ pad = _pad;
self.setMyUserInfo(myInitialUserInfo);
diff --git a/static/js/pad_utils.js b/static/js/pad_utils.js
index 8583ca9e..071185a8 100644
--- a/static/js/pad_utils.js
+++ b/static/js/pad_utils.js
@@ -34,7 +34,7 @@ var padutils = {
},
uniqueId: function()
{
- var pad = require('/pad2').pad; // Sidestep circular dependency
+ var pad = require('/pad').pad; // Sidestep circular dependency
function encodeNum(n, width)
{
// returns string that is exactly 'width' chars, padding with zeros
@@ -209,7 +209,7 @@ var padutils = {
},
timediff: function(d)
{
- var pad = require('/pad2').pad; // Sidestep circular dependency
+ var pad = require('/pad').pad; // Sidestep circular dependency
function format(n, word)
{
n = Math.round(n);
@@ -459,17 +459,25 @@ var padutils = {
}
};
-//send javascript errors to the server
-window.onerror = function test (msg, url, linenumber)
-{
- var errObj = {errorInfo: JSON.stringify({msg: msg, url: url, linenumber: linenumber, userAgent: navigator.userAgent})};
- var loc = document.location;
- var url = loc.protocol + "//" + loc.hostname + ":" + loc.port + "/" + loc.pathname.substr(1, loc.pathname.indexOf("/p/")) + "jserror";
+var globalExceptionHandler = undefined;
+function setupGlobalExceptionHandler() {
+ //send javascript errors to the server
+ if (!globalExceptionHandler) {
+ globalExceptionHandler = function test (msg, url, linenumber)
+ {
+ var errObj = {errorInfo: JSON.stringify({msg: msg, url: url, linenumber: linenumber, userAgent: navigator.userAgent})};
+ var loc = document.location;
+ var url = loc.protocol + "//" + loc.hostname + ":" + loc.port + "/" + loc.pathname.substr(1, loc.pathname.indexOf("/p/")) + "jserror";
- $.post(url, errObj);
+ $.post(url, errObj);
- return false;
-};
+ return false;
+ };
+ window.onerror = globalExceptionHandler;
+ }
+}
+
+padutils.setupGlobalExceptionHandler = setupGlobalExceptionHandler;
padutils.binarySearch = require('/ace2_common').binarySearch;
diff --git a/static/js/timeslider.js b/static/js/timeslider.js
new file mode 100644
index 00000000..939c4c64
--- /dev/null
+++ b/static/js/timeslider.js
@@ -0,0 +1,184 @@
+/**
+ * This code is mostly from the old Etherpad. Please help us to comment this code.
+ * This helps other people to understand this code better and helps them to improve it.
+ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
+ */
+
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * 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.
+ */
+
+// These jQuery things should create local references, but for now `require()`
+// assigns to the global `$` and augments it with plugins.
+require('/jquery');
+require('/json2');
+require('/undo-xpopup');
+
+function createCookie(name,value,days)
+{
+ if (days) {
+ var date = new Date();
+ date.setTime(date.getTime()+(days*24*60*60*1000));
+ var expires = "; expires="+date.toGMTString();
+ }
+ else var expires = "";
+ document.cookie = name+"="+value+expires+"; path=/";
+}
+
+function readCookie(name)
+{
+ var nameEQ = name + "=";
+ var ca = document.cookie.split(';');
+ for(var i=0;i < ca.length;i++) {
+ var c = ca[i];
+ while (c.charAt(0)==' ') c = c.substring(1,c.length);
+ if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
+ }
+ return null;
+}
+
+function randomString() {
+ var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+ var string_length = 20;
+ var randomstring = '';
+ for (var i=0; iYou have no permission to access this pad")
+ }
+ });
+
+ //get all the export links
+ export_links = $('#export > .exportlink')
+
+ if(document.referrer.length > 0 && document.referrer.substring(document.referrer.lastIndexOf("/")-1,document.referrer.lastIndexOf("/")) === "p") {
+ $("#returnbutton").attr("href", document.referrer);
+ } else {
+ $("#returnbutton").attr("href", document.location.href.substring(0,document.location.href.lastIndexOf("/")));
+ }
+ });
+}
+
+//sends a message over the socket
+function sendSocketMsg(type, data)
+{
+ var sessionID = readCookie("sessionID");
+ var password = readCookie("password");
+
+ var msg = { "component" : "timeslider",
+ "type": type,
+ "data": data,
+ "padId": padId,
+ "token": token,
+ "sessionID": sessionID,
+ "password": password,
+ "protocolVersion": 2};
+
+ socket.json.send(msg);
+}
+
+var fireWhenAllScriptsAreLoaded = [];
+
+var BroadcastSlider, changesetLoader;
+function handleClientVars(message)
+{
+ //save the client Vars
+ clientVars = message.data;
+
+ //load all script that doesn't work without the clientVars
+ BroadcastSlider = require('/broadcast_slider').loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded);
+ require('/broadcast_revisions').loadBroadcastRevisionsJS();
+ changesetLoader = require('/broadcast').loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, BroadcastSlider);
+
+ //initialize export ui
+ require('/pad_impexp').padimpexp.init();
+
+ //change export urls when the slider moves
+ var export_rev_regex = /(\/\d+)?\/export/
+ BroadcastSlider.onSlider(function(revno)
+ {
+ export_links.each(function()
+ {
+ this.setAttribute('href', this.href.replace(export_rev_regex, '/' + revno + '/export'));
+ });
+ });
+
+ //fire all start functions of these scripts, formerly fired with window.load
+ for(var i=0;i < fireWhenAllScriptsAreLoaded.length;i++)
+ {
+ fireWhenAllScriptsAreLoaded[i]();
+ }
+}
+
+exports.init = init;
diff --git a/static/pad.html b/static/pad.html
index 2d8889b1..e2e4690c 100644
--- a/static/pad.html
+++ b/static/pad.html
@@ -298,10 +298,14 @@