From 6af160ee560d43f574ab017df7df4baca3fd8ded Mon Sep 17 00:00:00 2001 From: mluto Date: Fri, 29 Mar 2013 10:09:39 +0100 Subject: [PATCH 001/263] Fix #1519: don't error in listSessionsOfGroup when there are non-existing sessions (by eldiddio) --- src/node/db/SessionManager.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/node/db/SessionManager.js b/src/node/db/SessionManager.js index 60e0a7ac..571ea07b 100644 --- a/src/node/db/SessionManager.js +++ b/src/node/db/SessionManager.js @@ -347,7 +347,15 @@ function listSessionsWithDBKey (dbkey, callback) { exports.getSessionInfo(sessionID, function(err, sessionInfo) { - if(ERR(err, callback)) return; + if (err == "apierror: sessionID does not exist") + { + console.warn("Found bad session " + sessionID + " in " + dbkey + "."); + } + else if(ERR(err, callback)) + { + return; + } + sessions[sessionID] = sessionInfo; callback(); }); From 716e929e64492858dc7ad4d507820c869afed6ce Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 29 Sep 2013 08:19:57 +0100 Subject: [PATCH 002/263] works but hides caret on full line and not on X too, X is much harder to do --- src/static/js/ace2_inner.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index f53e9de8..23c95c7d 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -3597,6 +3597,26 @@ function Ace2Inner(){ return; } + // Is caret potentially hidden by the chat button? + var myselection = document.getSelection(); // get the current caret selection + var caretOffsetTop = myselection.focusNode.parentNode.offsetTop | myselection.focusNode.offsetTop; // get the carets selection offset in px IE 214 + + if(myselection.focusNode.wholeText){ // Is there any content? If not lineHeight will report wrong.. + var lineHeight = myselection.focusNode.parentNode.offsetHeight; // line height of populated links + }else{ + var lineHeight = myselection.focusNode.offsetHeight; // line height of blank lines + } + var heightOfChatIcon = parent.parent.$('#chaticon').height(); // height of the chat icon button + lineHeight = (lineHeight *2) + heightOfChatIcon; + var viewport = getViewPortTopBottom(); + var viewportHeight = viewport.bottom - viewport.top - lineHeight; + var relCaretOffsetTop = caretOffsetTop - viewport.top; // relative Caret Offset Top to viewport + if (viewportHeight < relCaretOffsetTop){ + parent.parent.$("#chaticon").css("opacity",".3"); // make chaticon opacity low when user types near it + }else{ + parent.parent.$("#chaticon").css("opacity","1"); // make chaticon opacity back to full (so fully visible) + } + //dmesg("keyevent type: "+type+", which: "+which); // Don't take action based on modifier keys going up and down. // Modifier keys do not generate "keypress" events. From 383a216a7d41b8764da590183d5a58b65624b896 Mon Sep 17 00:00:00 2001 From: Grant Woodford Date: Thu, 28 Nov 2013 21:48:08 +0200 Subject: [PATCH 003/263] Added a way to closed the extended chat window --- src/static/js/chat.js | 6 +++--- src/templates/pad.html | 14 +++++++++++++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/static/js/chat.js b/src/static/js/chat.js index ed9b258f..2ae72fe3 100644 --- a/src/static/js/chat.js +++ b/src/static/js/chat.js @@ -41,15 +41,15 @@ var chat = (function() if(!isStuck || fromInitialCall) { // Stick it to padcookie.setPref("chatAlwaysVisible", true); $('#chatbox').addClass("stickyChat"); - $('#chattext').css({"top":"0px"}); + $('#titlesticky').hide(); $('#editorcontainer').css({"right":"192px"}); isStuck = true; } else { // Unstick it padcookie.setPref("chatAlwaysVisible", false); $('#chatbox').removeClass("stickyChat"); - $('#chattext').css({"top":"25px"}); + $('#titlesticky').show(); $('#editorcontainer').css({"right":"0px"}); - isStuck = false; + isStuck = false; } }, hide: function () diff --git a/src/templates/pad.html b/src/templates/pad.html index f6adfe9c..eaa949de 100644 --- a/src/templates/pad.html +++ b/src/templates/pad.html @@ -390,7 +390,7 @@
@@ -476,6 +476,18 @@ padeditbar = require('ep_etherpad-lite/static/js/pad_editbar').padeditbar; padimpexp = require('ep_etherpad-lite/static/js/pad_impexp').padimpexp; }()); + + function minimizeChatbox() + { + if ($('#options-stickychat').prop('checked')) { + chat.stickToScreen(); + $('#options-stickychat').prop('checked', false); + } else { + chat.hide(); + } + + return false; + } <% e.end_block(); %> From d1367484e3daa8bec76004dc9e1a827a231857ab Mon Sep 17 00:00:00 2001 From: Grant Woodford Date: Thu, 28 Nov 2013 21:53:00 +0200 Subject: [PATCH 004/263] Added a way to closed the extended chat window --- src/static/js/chat.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/static/js/chat.js b/src/static/js/chat.js index 2ae72fe3..ed01600f 100644 --- a/src/static/js/chat.js +++ b/src/static/js/chat.js @@ -41,15 +41,15 @@ var chat = (function() if(!isStuck || fromInitialCall) { // Stick it to padcookie.setPref("chatAlwaysVisible", true); $('#chatbox').addClass("stickyChat"); - $('#titlesticky').hide(); + $('#titlesticky').hide(); $('#editorcontainer').css({"right":"192px"}); isStuck = true; } else { // Unstick it padcookie.setPref("chatAlwaysVisible", false); $('#chatbox').removeClass("stickyChat"); - $('#titlesticky').show(); + $('#titlesticky').show(); $('#editorcontainer').css({"right":"0px"}); - isStuck = false; + isStuck = false; } }, hide: function () From 17666eaef78f1ffab70c446b5bddae2ffdbeefdd Mon Sep 17 00:00:00 2001 From: Grant Woodford Date: Sun, 15 Dec 2013 12:02:43 +0200 Subject: [PATCH 005/263] Changed to not use inline JavaScript --- src/static/js/chat.js | 17 ++++++++++++----- src/templates/pad.html | 14 +------------- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/src/static/js/chat.js b/src/static/js/chat.js index ed01600f..74df5fc2 100644 --- a/src/static/js/chat.js +++ b/src/static/js/chat.js @@ -54,11 +54,18 @@ var chat = (function() }, hide: function () { - $("#chatcounter").text("0"); - $("#chaticon").show(); - $("#chatbox").hide(); - $.gritter.removeAll(); - $("#gritter-notice-wrapper").show(); + // decide on hide logic based on chat window being maximized or not + if ($('#options-stickychat').prop('checked')) { + chat.stickToScreen(); + $('#options-stickychat').prop('checked', false); + } + else { + $("#chatcounter").text("0"); + $("#chaticon").show(); + $("#chatbox").hide(); + $.gritter.removeAll(); + $("#gritter-notice-wrapper").show(); + } }, scrollDown: function() { diff --git a/src/templates/pad.html b/src/templates/pad.html index eaa949de..f6adfe9c 100644 --- a/src/templates/pad.html +++ b/src/templates/pad.html @@ -390,7 +390,7 @@
@@ -476,18 +476,6 @@ padeditbar = require('ep_etherpad-lite/static/js/pad_editbar').padeditbar; padimpexp = require('ep_etherpad-lite/static/js/pad_impexp').padimpexp; }()); - - function minimizeChatbox() - { - if ($('#options-stickychat').prop('checked')) { - chat.stickToScreen(); - $('#options-stickychat').prop('checked', false); - } else { - chat.hide(); - } - - return false; - } <% e.end_block(); %> From 8245e65d3ffdc45772b5c776856bc1a3a05bd604 Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Thu, 20 Feb 2014 18:13:30 +0100 Subject: [PATCH 006/263] disconnect client if it submits an already accepted changeset based on an old revision --- src/node/handler/PadMessageHandler.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index 774ebf08..05705e01 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -682,6 +682,14 @@ function handleUserChanges(data, cb) // and can be applied after "c". try { + // a changeset can be based on an old revision with the same changes in it + // prevent eplite from accepting it TODO: better send the client a NEW_CHANGES + // of that revision + if(baseRev+1 == r && c == changeset) { + client.json.send({disconnect:"badChangeset"}); + stats.meter('failedChangesets').mark(); + return callback(new Error("Won't apply USER_CHANGES, because it contains an already accepted changeset")); + } changeset = Changeset.follow(c, changeset, false, apool); }catch(e){ client.json.send({disconnect:"badChangeset"}); From a4f2ebe57ee304ca5d8d415e4f4f1bab443f9fb8 Mon Sep 17 00:00:00 2001 From: jdittrich Date: Wed, 26 Mar 2014 18:22:41 +0100 Subject: [PATCH 007/263] Update package.json updated the underscore version number to 1.5.1 (from 1.3.1). Not lifted to most current version because frontend tests broke from 1.5.2 upwards. --- src/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/package.json b/src/package.json index f47ec474..d8aa26e9 100644 --- a/src/package.json +++ b/src/package.json @@ -34,7 +34,7 @@ "semver" : "1.0.13", "security" : "1.0.0", "tinycon" : "0.0.1", - "underscore" : "1.3.1", + "underscore" : "1.5.1", "unorm" : "1.0.0", "languages4translatewiki" : "0.1.3", "swagger-node-express" : "1.2.3", From 4ccd7131d31d931007f0606173bf212fefa30d79 Mon Sep 17 00:00:00 2001 From: Mike DeRosa Date: Sat, 14 Jun 2014 14:24:54 -0400 Subject: [PATCH 008/263] Added function to switch to a different pad without having to reload the whole page. --- src/node/handler/PadMessageHandler.js | 28 +++++++ src/static/js/chat.js | 4 + src/static/js/collab_client.js | 4 + src/static/js/pad.js | 114 +++++++++++++++++--------- 4 files changed, 109 insertions(+), 41 deletions(-) diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index 26eb17a6..fbf43599 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -217,6 +217,8 @@ exports.handleMessage = function(client, message) } else { messageLogger.warn("Dropped message, unknown COLLABROOM Data Type " + message.data.type); } + } else if(message.type == "CLEAR_SESSION_INFO") { + handleClearSessionInfo(client, message); } else { messageLogger.warn("Dropped message, unknown Message Type " + message.type); } @@ -872,6 +874,32 @@ function _correctMarkersInPad(atext, apool) { return builder.toString(); } +function handleClearSessionInfo(client, message) +{ + var infoMsg = { + type: "COLLABROOM", + data: { + type: "CLEAR_CHAT_MESSAGES" + } + }; + + // send the messages back to the client to clear the chat messages + client.json.send(infoMsg); + + // clear the session and leave the room + var currentSession = sessioninfos[client.id]; + var padId = currentSession.padId; + var roomClients = socketio.sockets.clients(padId); + for(var i = 0; i < roomClients.length; i++) { + var sinfo = sessioninfos[roomClients[i].id]; + if(sinfo && sinfo == currentSession) { + // fix user's counter, works on page refresh or if user closes browser window and then rejoins + sessioninfos[roomClients[i].id] = {}; + roomClients[i].leave(padId); + } + } +} + /** * Handles a CLIENT_READY. A CLIENT_READY is the first message from the client to the server. The Client sends his token * and the pad it wants to enter. The Server answers with the inital values (clientVars) of the pad diff --git a/src/static/js/chat.js b/src/static/js/chat.js index 65fc8dd9..76444690 100644 --- a/src/static/js/chat.js +++ b/src/static/js/chat.js @@ -79,6 +79,10 @@ var chat = (function() this._pad.collabClient.sendMessage({"type": "CHAT_MESSAGE", "text": text}); $("#chatinput").val(""); }, + clearChatMessages: function() + { + $('#chattext p').remove(); + }, addMessage: function(msg, increment, isHistoryAdd) { //correct the time diff --git a/src/static/js/collab_client.js b/src/static/js/collab_client.js index 146ec51b..420a6d4c 100644 --- a/src/static/js/collab_client.js +++ b/src/static/js/collab_client.js @@ -388,6 +388,10 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options, _pad) { chat.addMessage(msg, true, false); } + else if (msg.type == "CLEAR_CHAT_MESSAGES") + { + chat.clearChatMessages(); + } else if (msg.type == "CHAT_MESSAGES") { for(var i = msg.messages.length - 1; i >= 0; i--) diff --git a/src/static/js/pad.js b/src/static/js/pad.js index 73fcd3d6..994d7845 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.js @@ -51,6 +51,8 @@ var gritter = require('./gritter').gritter; var hooks = require('./pluginfw/hooks'); +var receivedClientVars = false; + function createCookie(name, value, days, path){ /* Warning Internet Explorer doesn't use this it uses the one from pad_utils.js */ if (days) { @@ -160,6 +162,59 @@ function savePassword() return false; } +function sendClearSessionInfo() +{ + var msg = { + "component": "pad", + "type": "CLEAR_SESSION_INFO", + "protocolVersion": 2 + }; + + socket.json.send(msg); +} + +function sendClientReady(isReconnect) +{ + var padId = document.location.pathname.substring(document.location.pathname.lastIndexOf("/") + 1); + padId = decodeURIComponent(padId); // unescape neccesary due to Safari and Opera interpretation of spaces + + if(!isReconnect) + { + var titleArray = document.title.split('|'); + var title = titleArray[titleArray.length - 1]; + document.title = padId.replace(/_+/g, ' ') + " | " + title; + } + + var token = readCookie("token"); + if (token == null) + { + token = "t." + randomString(); + createCookie("token", token, 60); + } + + var sessionID = decodeURIComponent(readCookie("sessionID")); + var password = readCookie("password"); + + var msg = { + "component": "pad", + "type": "CLIENT_READY", + "padId": padId, + "sessionID": sessionID, + "password": password, + "token": token, + "protocolVersion": 2 + }; + + //this is a reconnect, lets tell the server our revisionnumber + if(isReconnect == true) + { + msg.client_rev=pad.collabClient.getCurrentRevisionNumber(); + msg.reconnect=true; + } + + socket.json.send(msg); +} + function handshake() { var loc = document.location; @@ -176,44 +231,6 @@ function handshake() 'sync disconnect on unload' : false }); - function sendClientReady(isReconnect) - { - var padId = document.location.pathname.substring(document.location.pathname.lastIndexOf("/") + 1); - padId = decodeURIComponent(padId); // unescape neccesary due to Safari and Opera interpretation of spaces - - if(!isReconnect) - document.title = padId.replace(/_+/g, ' ') + " | " + document.title; - - var token = readCookie("token"); - if (token == null) - { - token = "t." + randomString(); - createCookie("token", token, 60); - } - - var sessionID = decodeURIComponent(readCookie("sessionID")); - var password = readCookie("password"); - - var msg = { - "component": "pad", - "type": "CLIENT_READY", - "padId": padId, - "sessionID": sessionID, - "password": password, - "token": token, - "protocolVersion": 2 - }; - - //this is a reconnect, lets tell the server our revisionnumber - if(isReconnect == true) - { - msg.client_rev=pad.collabClient.getCurrentRevisionNumber(); - msg.reconnect=true; - } - - socket.json.send(msg); - }; - var disconnectTimeout; socket.once('connect', function () { @@ -228,7 +245,7 @@ function handshake() } pad.collabClient.setChannelState("CONNECTED"); - sendClientReady(true); + pad.sendClientReady(true); }); socket.on('disconnect', function (reason) { @@ -246,7 +263,6 @@ function handshake() } }); - var receivedClientVars = false; var initalized = false; socket.on('message', function(obj) @@ -286,7 +302,7 @@ function handshake() } //if we haven't recieved the clientVars yet, then this message should it be - else if (!receivedClientVars) + else if (!receivedClientVars && obj.type == "CLIENT_VARS") { //log the message if (window.console) console.log(obj); @@ -426,6 +442,22 @@ var pad = { { return pad.myUserInfo.name; }, + sendClientReady: function(isReconnect) + { + sendClientReady(isReconnect); + }, + switchToPad: function(padId) + { + var options = document.location.href.split('?')[1]; + if(options != null) + window.history.pushState("", "", "/p/" + padId + '?' + options); + else + window.history.pushState("", "", "/p/" + padId); + + sendClearSessionInfo(); + receivedClientVars = false; + sendClientReady(false); + }, sendClientMessage: function(msg) { pad.collabClient.sendClientMessage(msg); From 070ba40f4f7c64f1ed14df13749f94b59b9ce961 Mon Sep 17 00:00:00 2001 From: Mike DeRosa Date: Sun, 6 Jul 2014 18:22:24 -0400 Subject: [PATCH 009/263] Fallback for browsers that don't support window.history.pushstate --- src/static/js/pad.js | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/static/js/pad.js b/src/static/js/pad.js index 994d7845..7d487b1c 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.js @@ -449,14 +449,21 @@ var pad = { switchToPad: function(padId) { var options = document.location.href.split('?')[1]; - if(options != null) - window.history.pushState("", "", "/p/" + padId + '?' + options); - else - window.history.pushState("", "", "/p/" + padId); - - sendClearSessionInfo(); - receivedClientVars = false; - sendClientReady(false); + var newHref = "/p/" + padId; + if (options != null) + newHref = newHref + '?' + options; + + if(window.history && window.history.pushState) + { + window.history.pushState("", "", newHref); + sendClearSessionInfo(); + receivedClientVars = false; + sendClientReady(false); + } + else // fallback + { + window.location.href = newHref; + } }, sendClientMessage: function(msg) { From 7861cae76325c4fbc07876d95a264310a5bdc574 Mon Sep 17 00:00:00 2001 From: Mike DeRosa Date: Sat, 12 Jul 2014 16:27:00 -0400 Subject: [PATCH 010/263] Cleaning up switchToPad functionality so that we only need one call to the server("SWITCH_TO_PAD") instead of two (cleaning session info and client ready). Also Clearing chat messages when switchToPad is called in pad.js instead of having the server tell the client to clear the chat messages. --- src/node/handler/PadMessageHandler.js | 49 +++++++++++++-------------- src/static/js/chat.js | 4 --- src/static/js/collab_client.js | 4 --- src/static/js/pad.js | 25 +++++--------- 4 files changed, 32 insertions(+), 50 deletions(-) diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index fbf43599..d0184bc2 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -217,8 +217,8 @@ exports.handleMessage = function(client, message) } else { messageLogger.warn("Dropped message, unknown COLLABROOM Data Type " + message.data.type); } - } else if(message.type == "CLEAR_SESSION_INFO") { - handleClearSessionInfo(client, message); + } else if(message.type == "SWITCH_TO_PAD") { + handleSwitchToPad(client, message); } else { messageLogger.warn("Dropped message, unknown Message Type " + message.type); } @@ -232,18 +232,7 @@ exports.handleMessage = function(client, message) { // client tried to auth for the first time (first msg from the client) if(message.type == "CLIENT_READY") { - // Remember this information since we won't - // have the cookie in further socket.io messages. - // This information will be used to check if - // the sessionId of this connection is still valid - // since it could have been deleted by the API. - sessioninfos[client.id].auth = - { - sessionID: message.sessionID, - padID: message.padId, - token : message.token, - password: message.password - }; + createSessionInfo(client, message); } // Note: message.sessionID is an entirely different kind of @@ -874,18 +863,8 @@ function _correctMarkersInPad(atext, apool) { return builder.toString(); } -function handleClearSessionInfo(client, message) +function handleSwitchToPad(client, message) { - var infoMsg = { - type: "COLLABROOM", - data: { - type: "CLEAR_CHAT_MESSAGES" - } - }; - - // send the messages back to the client to clear the chat messages - client.json.send(infoMsg); - // clear the session and leave the room var currentSession = sessioninfos[client.id]; var padId = currentSession.padId; @@ -898,6 +877,26 @@ function handleClearSessionInfo(client, message) roomClients[i].leave(padId); } } + + // start up the new pad + createSessionInfo(client, message); + handleClientReady(client, message); +} + +function createSessionInfo(client, message) +{ + // Remember this information since we won't + // have the cookie in further socket.io messages. + // This information will be used to check if + // the sessionId of this connection is still valid + // since it could have been deleted by the API. + sessioninfos[client.id].auth = + { + sessionID: message.sessionID, + padID: message.padId, + token : message.token, + password: message.password + }; } /** diff --git a/src/static/js/chat.js b/src/static/js/chat.js index 76444690..65fc8dd9 100644 --- a/src/static/js/chat.js +++ b/src/static/js/chat.js @@ -79,10 +79,6 @@ var chat = (function() this._pad.collabClient.sendMessage({"type": "CHAT_MESSAGE", "text": text}); $("#chatinput").val(""); }, - clearChatMessages: function() - { - $('#chattext p').remove(); - }, addMessage: function(msg, increment, isHistoryAdd) { //correct the time diff --git a/src/static/js/collab_client.js b/src/static/js/collab_client.js index 420a6d4c..146ec51b 100644 --- a/src/static/js/collab_client.js +++ b/src/static/js/collab_client.js @@ -388,10 +388,6 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options, _pad) { chat.addMessage(msg, true, false); } - else if (msg.type == "CLEAR_CHAT_MESSAGES") - { - chat.clearChatMessages(); - } else if (msg.type == "CHAT_MESSAGES") { for(var i = msg.messages.length - 1; i >= 0; i--) diff --git a/src/static/js/pad.js b/src/static/js/pad.js index 7d487b1c..5d1f1067 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.js @@ -162,19 +162,9 @@ function savePassword() return false; } -function sendClearSessionInfo() -{ - var msg = { - "component": "pad", - "type": "CLEAR_SESSION_INFO", - "protocolVersion": 2 - }; - - socket.json.send(msg); -} - -function sendClientReady(isReconnect) +function sendClientReady(isReconnect, messageType) { + messageType = typeof messageType !== 'undefined' ? messageType : 'CLIENT_READY'; var padId = document.location.pathname.substring(document.location.pathname.lastIndexOf("/") + 1); padId = decodeURIComponent(padId); // unescape neccesary due to Safari and Opera interpretation of spaces @@ -197,7 +187,7 @@ function sendClientReady(isReconnect) var msg = { "component": "pad", - "type": "CLIENT_READY", + "type": messageType, "padId": padId, "sessionID": sessionID, "password": password, @@ -442,9 +432,10 @@ var pad = { { return pad.myUserInfo.name; }, - sendClientReady: function(isReconnect) + sendClientReady: function(isReconnect, messageType) { - sendClientReady(isReconnect); + messageType = typeof messageType !== 'undefined' ? messageType : 'CLIENT_READY'; + sendClientReady(isReconnect, messageType); }, switchToPad: function(padId) { @@ -455,10 +446,10 @@ var pad = { if(window.history && window.history.pushState) { + $('#chattext p').remove(); //clear the chat messages window.history.pushState("", "", newHref); - sendClearSessionInfo(); receivedClientVars = false; - sendClientReady(false); + sendClientReady(false, 'SWITCH_TO_PAD'); } else // fallback { From 25f6c9bf9a6edb50dba5e8d6060364b3f7d1ceda Mon Sep 17 00:00:00 2001 From: Mike DeRosa Date: Sat, 12 Jul 2014 16:40:59 -0400 Subject: [PATCH 011/263] Checking if the author for the sessions match instead of comparing the entire session. --- src/node/handler/PadMessageHandler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index d0184bc2..7a82e4b1 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -871,7 +871,7 @@ function handleSwitchToPad(client, message) var roomClients = socketio.sockets.clients(padId); for(var i = 0; i < roomClients.length; i++) { var sinfo = sessioninfos[roomClients[i].id]; - if(sinfo && sinfo == currentSession) { + if(sinfo && sinfo.author == currentSession.author) { // fix user's counter, works on page refresh or if user closes browser window and then rejoins sessioninfos[roomClients[i].id] = {}; roomClients[i].leave(padId); From 17f26b8c2c981ee993f812510d68751ca03637c3 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 22 Jul 2014 15:46:31 +0100 Subject: [PATCH 012/263] Support to allow for a setting so you can stop unknown file types from being imported --- settings.json.template | 11 +++++++---- src/node/handler/ImportHandler.js | 11 ++++++++--- src/node/utils/Settings.js | 5 +++++ 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/settings.json.template b/settings.json.template index 5868af6a..359d1f8b 100644 --- a/settings.json.template +++ b/settings.json.template @@ -74,19 +74,22 @@ /* This is the path to the Abiword executable. Setting it to null, disables abiword. Abiword is needed to advanced import/export features of pads*/ "abiword" : null, + + /* Should we allow of file types other than the supported types: .html, .txt, .doc, .docx, .rtf, .ods */ + "allowUnknownFileEnds" : true, /* This setting is used if you require authentication of all users. Note: /admin always requires authentication. */ - "requireAuthentication": false, + "requireAuthentication" : false, /* Require authorization by a module, or a user with is_admin set, see below. */ - "requireAuthorization": false, + "requireAuthorization" : false, /*when you use NginX or another proxy/ load-balancer set this to true*/ - "trustProxy": false, + "trustProxy" : false, /* Privacy: disable IP logging */ - "disableIPlogging": false, + "disableIPlogging" : false, /* Users for basic authentication. is_admin = true gives access to /admin. If you do not uncomment this, /admin will not be available! */ diff --git a/src/node/handler/ImportHandler.js b/src/node/handler/ImportHandler.js index 60fa5ffb..8e724671 100644 --- a/src/node/handler/ImportHandler.js +++ b/src/node/handler/ImportHandler.js @@ -92,9 +92,14 @@ exports.doImport = function(req, res, padId) } //we need to rename this file with a .txt ending else { - var oldSrcFile = srcFile; - srcFile = path.join(path.dirname(srcFile),path.basename(srcFile, fileEnding)+".txt"); - fs.rename(oldSrcFile, srcFile, callback); + if(settings.allowUnknownFileEnds === true){ + var oldSrcFile = srcFile; + srcFile = path.join(path.dirname(srcFile),path.basename(srcFile, fileEnding)+".txt"); + fs.rename(oldSrcFile, srcFile, callback); + }else{ + console.warn("Not allowing unknown file type to be imported", fileEnding); + callback("uploadFailed"); + } } }, function(callback){ diff --git a/src/node/utils/Settings.js b/src/node/utils/Settings.js index 8b17a6d3..bbebf0bd 100644 --- a/src/node/utils/Settings.js +++ b/src/node/utils/Settings.js @@ -129,6 +129,11 @@ exports.minify = true; */ exports.abiword = null; +/** + * Should we support none natively supported file types on import? + */ +exports.allowUnknownFileEnds = true; + /** * The log level of log4js */ From aa908ea8ce5ba7aab20a3d78324e4a01a2d49fe5 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 22 Jul 2014 16:02:22 +0100 Subject: [PATCH 013/263] tidy up template a bit --- settings.json.template | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.json.template b/settings.json.template index 359d1f8b..b55ca7e0 100644 --- a/settings.json.template +++ b/settings.json.template @@ -75,7 +75,7 @@ Abiword is needed to advanced import/export features of pads*/ "abiword" : null, - /* Should we allow of file types other than the supported types: .html, .txt, .doc, .docx, .rtf, .ods */ + /* Should we allow of file types other than the supported types: html, txt, doc, docx, rtf, odt, html & htm */ "allowUnknownFileEnds" : true, /* This setting is used if you require authentication of all users. From 6dfe33258a4a211d1b9723200a5e877835b56393 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 22 Jul 2014 16:02:56 +0100 Subject: [PATCH 014/263] tidy up template a bit --- settings.json.template | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.json.template b/settings.json.template index b55ca7e0..8b2a9c2a 100644 --- a/settings.json.template +++ b/settings.json.template @@ -75,7 +75,7 @@ Abiword is needed to advanced import/export features of pads*/ "abiword" : null, - /* Should we allow of file types other than the supported types: html, txt, doc, docx, rtf, odt, html & htm */ + /* Should we allow of file types other than the supported types: txt, doc, docx, rtf, odt, html & htm */ "allowUnknownFileEnds" : true, /* This setting is used if you require authentication of all users. From 95ab126fe23fa72ee8ee015b7b6da93407988f6e Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 22 Jul 2014 16:03:26 +0100 Subject: [PATCH 015/263] tidy up template a bit --- settings.json.template | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.json.template b/settings.json.template index 8b2a9c2a..5b4155bf 100644 --- a/settings.json.template +++ b/settings.json.template @@ -75,7 +75,7 @@ Abiword is needed to advanced import/export features of pads*/ "abiword" : null, - /* Should we allow of file types other than the supported types: txt, doc, docx, rtf, odt, html & htm */ + /* Should we allow import of file types other than the supported types: txt, doc, docx, rtf, odt, html & htm */ "allowUnknownFileEnds" : true, /* This setting is used if you require authentication of all users. From 83b7ca529bdce7741c69b9a7da958133180ec30f Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 22 Jul 2014 16:03:49 +0100 Subject: [PATCH 016/263] tidy up template a bit --- settings.json.template | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.json.template b/settings.json.template index 5b4155bf..3f84af9b 100644 --- a/settings.json.template +++ b/settings.json.template @@ -75,7 +75,7 @@ Abiword is needed to advanced import/export features of pads*/ "abiword" : null, - /* Should we allow import of file types other than the supported types: txt, doc, docx, rtf, odt, html & htm */ + /* Allow import of file types other than the supported types: txt, doc, docx, rtf, odt, html & htm */ "allowUnknownFileEnds" : true, /* This setting is used if you require authentication of all users. From 287a7805c9c47dea2290ea8492df7e50155862f3 Mon Sep 17 00:00:00 2001 From: Stefan Date: Sat, 9 Aug 2014 14:05:04 +0200 Subject: [PATCH 017/263] Fix position of import/export popup in timeslider --- src/static/css/timeslider.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/static/css/timeslider.css b/src/static/css/timeslider.css index f97d4f2b..9f5b570d 100644 --- a/src/static/css/timeslider.css +++ b/src/static/css/timeslider.css @@ -168,8 +168,9 @@ width: 185px } #importexport { - top: 118px; + top: 100px; width: 185px; + position: fixed; } .timeslider-bar { background: #f7f7f7; From bb21759d787e1c1fb2c277f6137c74bc06e38372 Mon Sep 17 00:00:00 2001 From: Stefan Date: Sat, 9 Aug 2014 18:52:44 +0200 Subject: [PATCH 018/263] Fix timeslider revision not changing in most cases --- src/static/js/broadcast.js | 40 ++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/src/static/js/broadcast.js b/src/static/js/broadcast.js index d4bda111..9ac8ca3d 100644 --- a/src/static/js/broadcast.js +++ b/src/static/js/broadcast.js @@ -384,22 +384,10 @@ function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro } if (changeset) applyChangeset(changeset, path.rev, true, timeDelta); - - if (BroadcastSlider.getSliderLength() > 10000) - { - var start = (Math.floor((newRevision) / 10000) * 10000); // revision 0 to 10 - changesetLoader.queueUp(start, 100); - } - - if (BroadcastSlider.getSliderLength() > 1000) - { - var start = (Math.floor((newRevision) / 1000) * 1000); // (start from -1, go to 19) + 1 - changesetLoader.queueUp(start, 10); - } - - start = (Math.floor((newRevision) / 100) * 100); - - changesetLoader.queueUp(start, 1, update); + // Loading changeset history for new revision + loadChangesetsForRevision(newRevision, update); + // Loading changeset history for old revision (to make diff between old and new revision) + loadChangesetsForRevision(padContents.currentRevision - 1); } var authors = _.map(padContents.getActiveAuthors(), function(name){ @@ -407,6 +395,24 @@ function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro }); BroadcastSlider.setAuthors(authors); } + + function loadChangesetsForRevision(revision, callback) { + if (BroadcastSlider.getSliderLength() > 10000) + { + var start = (Math.floor((revision) / 10000) * 10000); // revision 0 to 10 + changesetLoader.queueUp(start, 100); + } + + if (BroadcastSlider.getSliderLength() > 1000) + { + var start = (Math.floor((revision) / 1000) * 1000); // (start from -1, go to 19) + 1 + changesetLoader.queueUp(start, 10); + } + + start = (Math.floor((revision) / 100) * 100); + + changesetLoader.queueUp(start, 1, callback); + } changesetLoader = { running: false, @@ -482,7 +488,7 @@ function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro var astart = start + i * granularity - 1; // rev -1 is a blank single line var aend = start + (i + 1) * granularity - 1; // totalRevs is the most recent revision if (aend > data.actualEndNum - 1) aend = data.actualEndNum - 1; - debugLog("adding changeset:", astart, aend); + //debugLog("adding changeset:", astart, aend); var forwardcs = Changeset.moveOpsToNewPool(data.forwardsChangesets[i], pool, padContents.apool); var backwardcs = Changeset.moveOpsToNewPool(data.backwardsChangesets[i], pool, padContents.apool); revisionInfo.addChangeset(astart, aend, forwardcs, backwardcs, data.timeDeltas[i]); From cf6e5aa3ca202c8e0f0f7e14c2b7fc4b551bbe7f Mon Sep 17 00:00:00 2001 From: Stefan Date: Sat, 9 Aug 2014 18:56:18 +0200 Subject: [PATCH 019/263] Fix click on timeslider loads two different revisions --- src/static/js/broadcast_slider.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/static/js/broadcast_slider.js b/src/static/js/broadcast_slider.js index 8179b7b5..0b0147dd 100644 --- a/src/static/js/broadcast_slider.js +++ b/src/static/js/broadcast_slider.js @@ -334,7 +334,6 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded) $("#ui-slider-bar").mousedown(function(evt) { - setSliderPosition(Math.floor((evt.clientX - $("#ui-slider-bar").offset().left) * sliderLength / 742)); $("#ui-slider-handle").css('left', (evt.clientX - $("#ui-slider-bar").offset().left)); $("#ui-slider-handle").trigger(evt); }); From 0d95c8d1ec51b5bbb3e1f6c3ae8f36e087298f18 Mon Sep 17 00:00:00 2001 From: Stefan Date: Sun, 14 Sep 2014 20:26:35 +0200 Subject: [PATCH 020/263] Revert change on position of imp/exp overlay --- src/static/css/timeslider.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/static/css/timeslider.css b/src/static/css/timeslider.css index 9f5b570d..87944a87 100644 --- a/src/static/css/timeslider.css +++ b/src/static/css/timeslider.css @@ -168,7 +168,7 @@ width: 185px } #importexport { - top: 100px; + top: 118px; width: 185px; position: fixed; } From 72a328cf118198bb101a40cb67dca5bb7f6455f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20=C5=A0uppa?= Date: Tue, 23 Sep 2014 16:04:22 +0200 Subject: [PATCH 021/263] readme: Add formatting sugar for readibility * Added a bit more formatting for UNIX-like systems dependencies installation. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 34a824ff..71c0b3e1 100644 --- a/README.md +++ b/README.md @@ -50,9 +50,9 @@ Update to the latest version with `git pull origin`, then run `bin\installOnWind ## GNU/Linux and other UNIX-like systems You'll need gzip, git, curl, libssl develop libraries, python and gcc. -*For Debian/Ubuntu*: `apt-get install gzip git-core curl python libssl-dev pkg-config build-essential` -*For Fedora/CentOS*: `yum install gzip git-core curl python openssl-devel && yum groupinstall "Development Tools"` -*For FreeBSD*: `portinstall node, npm, git (optional)` +- *For Debian/Ubuntu*: `apt-get install gzip git-core curl python libssl-dev pkg-config build-essential` +- *For Fedora/CentOS*: `yum install gzip git-core curl python openssl-devel && yum groupinstall "Development Tools"` +- *For FreeBSD*: `portinstall node, npm, git (optional)` Additionally, you'll need [node.js](http://nodejs.org) installed, Ideally the latest stable version, be careful of installing nodejs from apt. From 94968e69be03a78f6202b83fc958be292244f677 Mon Sep 17 00:00:00 2001 From: Stefan Date: Sat, 27 Sep 2014 12:30:11 +0200 Subject: [PATCH 022/263] Add support for npm 2 --- bin/installDeps.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bin/installDeps.sh b/bin/installDeps.sh index 84f2e227..a8bc88a8 100755 --- a/bin/installDeps.sh +++ b/bin/installDeps.sh @@ -36,8 +36,9 @@ hash npm > /dev/null 2>&1 || { #check npm version NPM_VERSION=$(npm --version) -if [ ! $(echo $NPM_VERSION | cut -d "." -f 1) = "1" ]; then - echo "You're running a wrong version of npm, you're using $NPM_VERSION, we need 1.x" >&2 +NPM_MAIN_VERSION=$(echo $NPM_VERSION | cut -d "." -f 1) +if [ $(echo $NPM_MAIN_VERSION) = "0" ]; then + echo "You're running a wrong version of npm, you're using $NPM_VERSION, we need 1.x or higher" >&2 exit 1 fi From bfde869948ecd7a40f99467831caaa22877d3dec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20T=C3=A9tard?= Date: Sun, 28 Sep 2014 14:53:07 +0200 Subject: [PATCH 023/263] =?UTF-8?q?If=20exportFileName=20hook=20isn?= =?UTF-8?q?=E2=80=99t=20defined=20don=E2=80=99t=20return=20empty=20filenam?= =?UTF-8?q?e.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit should fix #2251. If ``exportFileName`` hook is not defined, ``hookFileName`` should be an empty array. Test the length of ``hookFileName`` before overriding ``fileName``, the export filename. --- src/node/handler/ExportHandler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node/handler/ExportHandler.js b/src/node/handler/ExportHandler.js index a748d3f2..5bedcce2 100644 --- a/src/node/handler/ExportHandler.js +++ b/src/node/handler/ExportHandler.js @@ -52,7 +52,7 @@ exports.doExport = function(req, res, padId, type) hooks.aCallFirst("exportFileName", padId, function(err, hookFileName){ // if fileName is set then set it to the padId, note that fileName is returned as an array. - if(hookFileName) fileName = hookFileName; + if(hookFileName.length) fileName = hookFileName; //tell the browser that this is a downloadable file From 12c1d3b05a7ed4b044a54fb842ae171885cdc920 Mon Sep 17 00:00:00 2001 From: 0ip Date: Fri, 3 Oct 2014 17:35:48 +0200 Subject: [PATCH 024/263] Fix active button ui --- src/static/css/pad.css | 19 ++++--------------- src/static/css/timeslider.css | 11 +++++++---- src/static/js/pad_editbar.js | 8 ++++---- src/templates/pad.html | 2 +- src/templates/timeslider.html | 2 +- 5 files changed, 17 insertions(+), 25 deletions(-) diff --git a/src/static/css/pad.css b/src/static/css/pad.css index 4053ebba..979eb1ef 100644 --- a/src/static/css/pad.css +++ b/src/static/css/pad.css @@ -160,11 +160,11 @@ a img { border: 1px solid #ccc; outline: none; } -#usericon a { +li[data-key=showusers] > a { min-width: 30px; text-align: left; } -#usericon a #online_count { +li[data-key=showusers] > a #online_count { color: #777; font-size: 10px; position: relative; @@ -814,7 +814,7 @@ input[type=checkbox] { float: left; } #settings, -#importexport, +#import_export, #embed, #connectivity, #users { @@ -1070,15 +1070,4 @@ input[type=checkbox] { text-shadow: none; } -/* End of gritter stuff */ -.activeButton{ - background: #eee; - background: -webkit-linear-gradient(#ddd, #fff); - background: -moz-linear-gradient(#ddd, #fff); - background: -o-linear-gradient(#ddd, #fff); - background: -ms-linear-gradient(#ddd, #fff); - background: linear-gradient(#ddd, #fff); - -webkit-box-shadow: 0 0 8px rgba(0,0,0,.1) inset; - -moz-box-shadow: 0 0 8px rgba(0,0,0,.1) inset; - box-shadow: 0 0 8px rgba(0,0,0,.1) inset; -} +/* End of gritter stuff */ \ No newline at end of file diff --git a/src/static/css/timeslider.css b/src/static/css/timeslider.css index f97d4f2b..19c4ca9f 100644 --- a/src/static/css/timeslider.css +++ b/src/static/css/timeslider.css @@ -158,16 +158,19 @@ float: right } #settings, -#importexport, +#import_export, #embed, #connectivity, #users { top: 62px; } -#importexport .popup { - width: 185px +#import_export .popup { + width: 183px; + border-top: none; + border-right: none; + border-radius: 0 0 0 6px; } -#importexport { +#import_export { top: 118px; width: 185px; } diff --git a/src/static/js/pad_editbar.js b/src/static/js/pad_editbar.js index 85963217..6352b129 100644 --- a/src/static/js/pad_editbar.js +++ b/src/static/js/pad_editbar.js @@ -206,7 +206,7 @@ var padeditbar = (function() if(module.css('display') != "none") { - $("#" + self.dropdowns[i] + "link").removeClass("selected"); + $("li[data-key=" + self.dropdowns[i] + "] > a").removeClass("selected"); module.slideUp("fast", cb); returned = true; } @@ -223,12 +223,12 @@ var padeditbar = (function() if(module.css('display') != "none") { - $("#" + self.dropdowns[i] + "link").removeClass("selected"); + $("li[data-key=" + self.dropdowns[i] + "] > a").removeClass("selected"); module.slideUp("fast"); } else if(self.dropdowns[i]==moduleName) { - $("#" + self.dropdowns[i] + "link").addClass("selected"); + $("li[data-key=" + self.dropdowns[i] + "] > a").addClass("selected"); module.slideDown("fast", cb); } } @@ -271,7 +271,7 @@ var padeditbar = (function() toolbar.registerDropdownCommand("showusers", "users"); toolbar.registerDropdownCommand("settings"); toolbar.registerDropdownCommand("connectivity"); - toolbar.registerDropdownCommand("import_export", "importexport"); + toolbar.registerDropdownCommand("import_export"); toolbar.registerDropdownCommand("embed"); toolbar.registerCommand("embed", function () { diff --git a/src/templates/pad.html b/src/templates/pad.html index 50fac86b..a4366bd5 100644 --- a/src/templates/pad.html +++ b/src/templates/pad.html @@ -174,7 +174,7 @@
- From 21ce9b4dd38ef7cbe76ad2a3c3cb2ea2909d8148 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 18 Nov 2014 18:44:45 +0000 Subject: [PATCH 099/263] correct font stuff --- src/static/css/fontawesome-etherpad.css | 276 ++++++++++++++++------ src/static/css/pad.css | 120 +++++----- src/static/font/fontawesome-etherpad.eot | Bin 15442 -> 18372 bytes src/static/font/fontawesome-etherpad.svg | 125 +++++----- src/static/font/fontawesome-etherpad.ttf | Bin 15224 -> 18156 bytes src/static/font/fontawesome-etherpad.woff | Bin 10076 -> 10724 bytes 6 files changed, 331 insertions(+), 190 deletions(-) diff --git a/src/static/css/fontawesome-etherpad.css b/src/static/css/fontawesome-etherpad.css index 13f28f2b..f129e86d 100644 --- a/src/static/css/fontawesome-etherpad.css +++ b/src/static/css/fontawesome-etherpad.css @@ -1,76 +1,208 @@ +@charset "UTF-8"; + @font-face { - font-family: 'fontawesome-etherpad'; - src: url('../font/fontawesome-etherpad.eot?81419457'); - src: url('../font/fontawesome-etherpad.eot?81419457#iefix') format('embedded-opentype'), - url('../font/fontawesome-etherpad.woff?81419457') format('woff'), - url('../font/fontawesome-etherpad.ttf?81419457') format('truetype'), - url('../font/fontawesome-etherpad.svg?81419457#fontawesome-etherpad') format('svg'); - font-weight: normal; - font-style: normal; -} -/* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ -/* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ -/* -@media screen and (-webkit-min-device-pixel-ratio:0) { - @font-face { - font-family: 'fontawesome-etherpad'; - src: url('../font/fontawesome-etherpad.svg?81419457#fontawesome-etherpad') format('svg'); - } -} -*/ - - [class^="icon-"]:before, [class*=" icon-"]:before { font-family: "fontawesome-etherpad"; - font-style: normal; + src:url("../font/fontawesome-etherpad.eot"); + src:url("../font/fontawesome-etherpad.eot?#iefix") format("embedded-opentype"), + url("../font/fontawesome-etherpad.woff") format("woff"), + url("../font/fontawesome-etherpad.ttf") format("truetype"), + url("../font/fontawesome-etherpad.svg#fontawesome-etherpad") format("svg"); font-weight: normal; - speak: none; - - display: inline-block; - text-decoration: inherit; - width: 1em; - margin-right: .2em; - text-align: center; - /* opacity: .8; */ - - /* For safety - reset parent styles, that can break glyph codes*/ - font-variant: normal; - text-transform: none; - - /* fix buttons height, for twitter bootstrap */ - line-height: 1em; - - /* Animation center compensation - margins should be symmetric */ - /* remove if not needed */ - margin-left: .2em; - - /* you can be more comfortable with increased icons size */ - /* font-size: 120%; */ - - /* Uncomment for 3D effect */ - /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ + font-style: normal; + +} + +[data-icon]:before { + font-family: "fontawesome-etherpad" !important; + content: attr(data-icon); + font-style: normal !important; + font-weight: normal !important; + font-variant: normal !important; + text-transform: none !important; + speak: none; + line-height: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +[class^="icon-"]:before, +[class*=" icon-"]:before { + font-family: "fontawesome-etherpad" !important; + font-style: normal !important; + font-weight: normal !important; + font-variant: normal !important; + text-transform: none !important; + speak: none; + line-height: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.icon-clock:before { + content: "a"; +} +.icon-mic-no:before { + content: "b"; +} +.icon-text-height:before { + content: "c"; +} +.icon-text-width:before { + content: "d"; +} +.icon-talk-chat:before { + content: "e"; +} +.icon-talk-chat-2:before { + content: "f"; +} +.icon-video-camera:before { + content: "g"; +} +.icon-user:before { + content: "h"; +} +.icon-bold:before { + content: "i"; +} +.icon-italic:before { + content: "j"; +} +.icon-trash-bin:before { + content: "k"; +} +.icon-strikethrough:before { + content: "l"; +} +.icon-tachometer:before { + content: "m"; +} +.icon-letter-mail:before { + content: "n"; +} +.icon-font:before { + content: "o"; +} +.icon-underline:before { + content: "p"; +} +.icon-list:before { + content: "q"; +} +.icon-list-number:before { + content: "r"; +} +.icon-indent:before { + content: "s"; +} +.icon-outdent:before { + content: "t"; +} +.icon-undo:before { + content: "u"; +} +.icon-spinner:before { + content: "v"; +} +.icon-repeat-redo:before { + content: "w"; +} +.icon-eye-slash-close:before { + content: "x"; +} +.icon-volume-up:before { + content: "y"; +} +.icon-paper-clip:before { + content: "z"; +} +.icon-exchange:before { + content: "C"; +} +.icon-cog-gear:before { + content: "D"; +} +.icon-code:before { + content: "E"; +} +.icon-play:before { + content: "F"; +} +.icon-backward:before { + content: "G"; +} +.icon-forward:before { + content: "H"; +} +.icon-step-forward:before { + content: "I"; +} +.icon-step-backward:before { + content: "J"; +} +.icon-align-justify:before { + content: "A"; +} +.icon-align-left:before { + content: "B"; +} +.icon-align-right:before { + content: "K"; +} +.icon-align-center:before { + content: "L"; +} +.icon-subscript:before { + content: "M"; +} +.icon-superscript:before { + content: "N"; +} +.icon-file:before { + content: "O"; +} +.icon-file-document:before { + content: "P"; +} +.icon-file-text:before { + content: "Q"; +} +.icon-files:before { + content: "R"; +} +.icon-filmstrip:before { + content: "S"; +} +.icon-folder2:before { + content: "T"; +} +.icon-list-bullet:before { + content: "U"; +} +.icon-pencil:before { + content: "V"; +} +.icon-pause:before { + content: "W"; +} +.icon-phone-mobile:before { + content: "X"; +} +.icon-mic:before { + content: "Y"; +} +.icon-camera:before { + content: "Z"; +} +.icon-chat-bubble:before { + content: "0"; +} +.icon-chat-bubble-two:before { + content: "1"; +} +.icon-ptint:before { + content: "2"; +} +.icon-group:before { + content: "3"; } - -.icon-users:before { content: '\e800'; } /* '' */ -.icon-star:before { content: '\e801'; } /* '' */ -.icon-cog:before { content: '\e802'; } /* '' */ -.icon-bold:before { content: '\e803'; } /* '' */ -.icon-italic:before { content: '\e804'; } /* '' */ -.icon-indent-left:before { content: '\e805'; } /* '' */ -.icon-indent-right:before { content: '\e806'; } /* '' */ -.icon-list-bullet:before { content: '\e807'; } /* '' */ -.icon-list-numbered:before { content: '\e808'; } /* '' */ -.icon-strike:before { content: '\e809'; } /* '' */ -.icon-underline:before { content: '\e80a'; } /* '' */ -.icon-cw:before { content: '\e80b'; } /* '' */ -.icon-ccw:before { content: '\e80c'; } /* '' */ -.icon-clock:before { content: '\e80d'; } /* '' */ -.icon-eye-off:before { content: '\e80e'; } /* '' */ -.icon-eye:before { content: '\e80f'; } /* '' */ -.icon-play:before { content: '\e810'; } /* '' */ -.icon-fast-bw:before { content: '\e811'; } /* '' */ -.icon-fast-fw:before { content: '\e812'; } /* '' */ -.icon-pause:before { content: '\e813'; } /* '' */ -.icon-glass:before { content: '\e814'; } /* '' */ -.icon-code:before { content: '\e815'; } /* '' */ -.icon-exchange:before { content: '\e816'; } /* '' */ -.icon-chat:before { content: '\e817'; } /* '' */ \ No newline at end of file diff --git a/src/static/css/pad.css b/src/static/css/pad.css index 55a24733..f97e1b3e 100644 --- a/src/static/css/pad.css +++ b/src/static/css/pad.css @@ -611,28 +611,36 @@ table#otheruserstable { margin-top: 4px; background-repeat: no-repeat; padding-left: 25px; - background-image: url("../../static/img/etherpad_lite_icons.png"); color: #333; text-decoration: none; padding-bottom:2px; + display:inline; + padding-left:5px; + font-family: "Arial"; } -#exporthtml { - background-position: 0px -299px +.exportlink{ + font-family: "fontawesome-etherpad"; + display:block; + margin:5px; + color:#666; } -#exportplain { - background-position: 0px -395px +#exporthtmla:before { + content: "\e826"; } -#exportword { - background-position: 0px -275px +#exportplaina:before { + content: "\e802"; } -#exportpdf { - background-position: 0px -371px +#exportworda:before { + content: "\e804"; } -#exportopen { - background-position: 0px -347px +#exportpdfa:before { + content: "\e803"; } -#exportdokuwiki { - background-position: 0px -459px +#exportopena:before { + content: "\e805"; +} +#exportdokuwikia:before { + content: "\e805"; } /* hidden element */ @@ -692,58 +700,58 @@ table#otheruserstable { color: #666; } .buttonicon-bold:before { - content: "\e803"; + content: "\e81c"; } .buttonicon-italic:before { - content: "\e804"; + content: "\e81d"; } .buttonicon-underline:before { - content: "\e80a"; -} -.buttonicon-strikethrough:before { - content: "\e809"; -} -.buttonicon-insertorderedlist:before { - content: "\e808"; -} -.buttonicon-insertunorderedlist:before { - content: "\e807"; -} -.buttonicon-indent:before { - content: "\e806"; -} -.buttonicon-outdent:before { - content: "\e805"; -} -.buttonicon-undo:before { - content: "\e80c"; -} -.buttonicon-redo:before { - content: "\e80b"; -} -.buttonicon-clearauthorship:before { - content: "\e80e"; -} -.buttonicon-settings:before { - content: "\e802"; -} -.buttonicon-import_export:before { - content: "\e816"; -} -.buttonicon-embed:before { - content: "\e815"; -} -.buttonicon-history:before { - content: "\e80d"; -} -.buttonicon-chat:before { content: "\e817"; } +.buttonicon-strikethrough:before { + content: "\e818"; +} +.buttonicon-insertorderedlist:before { + content: "\e816"; +} +.buttonicon-insertunorderedlist:before { + content: "\e815"; +} +.buttonicon-indent:before { + content: "\e814"; +} +.buttonicon-outdent:before { + content: "\e813"; +} +.buttonicon-undo:before { + content: "\e823"; +} +.buttonicon-redo:before { + content: "\e824"; +} +.buttonicon-clearauthorship:before { + content: "\e80d"; +} +.buttonicon-settings:before { + content: "\e833"; +} +.buttonicon-import_export:before { + content: "\e834"; +} +.buttonicon-embed:before { + content: "\e827"; +} +.buttonicon-history:before { + content: "\e837"; +} +.buttonicon-chat:before { + content: "\e829"; +} .buttonicon-showusers:before { - content: "\e800"; + content: "\e808"; } .buttonicon-savedRevision:before { - content: "\e801"; + content: "\e835"; } #focusprotector { z-index: 100; diff --git a/src/static/font/fontawesome-etherpad.eot b/src/static/font/fontawesome-etherpad.eot index cc934ce676862037a6df6a17bf934512bb965083..fb65168629fd9d9a4bc0e32665b1055db61af150 100644 GIT binary patch literal 18372 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 -Generated by Fontastic.me +Copyright (C) 2014 by original authors @ fontello.com - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/static/font/fontawesome-etherpad.ttf b/src/static/font/fontawesome-etherpad.ttf index bbfd72170107b1eeb77dbef5373126cd071f6734..f2fe398ec9f8fe56f220792e119b7014e8f54bf9 100644 GIT binary patch literal 18156 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 literal 15224 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 diff --git a/src/static/font/fontawesome-etherpad.woff b/src/static/font/fontawesome-etherpad.woff index 491fc501679b119445df29104082e5ae99d4d6ff..98de9c3e6673f1f0ce120fe83b1ed67a86d502d7 100644 GIT binary patch literal 10724 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 literal 10076 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 aIa@bOP`AbY|KEcWh_UFl?<5|8@P7btTjlux From 3ad9a58895090a6fad0ce5930cefdcfd89b64312 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 18 Nov 2014 18:47:12 +0000 Subject: [PATCH 100/263] fontello config file --- src/static/font/config.json | 346 ++++++++++++++++++++++++++++++++++++ 1 file changed, 346 insertions(+) create mode 100644 src/static/font/config.json diff --git a/src/static/font/config.json b/src/static/font/config.json new file mode 100644 index 00000000..34231cca --- /dev/null +++ b/src/static/font/config.json @@ -0,0 +1,346 @@ +{ + "name": "fontawesome-etherpad", + "css_prefix_text": "icon-", + "css_use_suffix": false, + "hinting": true, + "units_per_em": 1000, + "ascent": 850, + "glyphs": [ + { + "uid": "bf882b30900da12fca090d9796bc3030", + "css": "mail", + "code": 59402, + "src": "fontawesome" + }, + { + "uid": "474656633f79ea2f1dad59ff63f6bf07", + "css": "star", + "code": 59446, + "src": "fontawesome" + }, + { + "uid": "d17030afaecc1e1c22349b99f3c4992a", + "css": "star-empty", + "code": 59445, + "src": "fontawesome" + }, + { + "uid": "8b80d36d4ef43889db10bc1f0dc9a862", + "css": "user", + "code": 59401, + "src": "fontawesome" + }, + { + "uid": "31972e4e9d080eaa796290349ae6c1fd", + "css": "users", + "code": 59400, + "src": "fontawesome" + }, + { + "uid": "0f99ab40ab0b4d64a74f2d0deeb03e42", + "css": "videocam", + "code": 59403, + "src": "fontawesome" + }, + { + "uid": "381da2c2f7fd51f8de877c044d7f439d", + "css": "picture", + "code": 59404, + "src": "fontawesome" + }, + { + "uid": "7fd683b2c518ceb9e5fa6757f2276faa", + "css": "eye-off", + "code": 59405, + "src": "fontawesome" + }, + { + "uid": "7034e4d22866af82bef811f52fb1ba46", + "css": "code", + "code": 59431, + "src": "fontawesome" + }, + { + "uid": "7277ded7695b2a307a5f9d50097bb64c", + "css": "print", + "code": 59393, + "src": "fontawesome" + }, + { + "uid": "dcedf50ab1ede3283d7a6c70e2fe32f3", + "css": "chat", + "code": 59432, + "src": "fontawesome" + }, + { + "uid": "9c1376672bb4f1ed616fdd78a23667e9", + "css": "comment-empty", + "code": 59433, + "src": "fontawesome" + }, + { + "uid": "f48ae54adfb27d8ada53d0fd9e34ee10", + "css": "trash-empty", + "code": 59434, + "src": "fontawesome" + }, + { + "uid": "1b5a5d7b7e3c71437f5a26befdd045ed", + "css": "doc", + "code": 59394, + "src": "fontawesome" + }, + { + "uid": "9daa1fdf0838118518a7e22715e83abc", + "css": "file-pdf", + "code": 59395, + "src": "fontawesome" + }, + { + "uid": "310ffd629da85142bc8669f010556f2d", + "css": "file-word", + "code": 59396, + "src": "fontawesome" + }, + { + "uid": "f761c3bbe16ba2d332914ecb28e7a042", + "css": "file-excel", + "code": 59397, + "src": "fontawesome" + }, + { + "uid": "edcd4022de8d8df266ef7c42d2658ca5", + "css": "file-powerpoint", + "code": 59398, + "src": "fontawesome" + }, + { + "uid": "3c961c1a8d874815856fc6637dc5a13c", + "css": "file-image", + "code": 59399, + "src": "fontawesome" + }, + { + "uid": "26613a2e6bc41593c54bead46f8c8ee3", + "css": "file-code", + "code": 59430, + "src": "fontawesome" + }, + { + "uid": "e99461abfef3923546da8d745372c995", + "css": "cog", + "code": 59443, + "src": "fontawesome" + }, + { + "uid": "19c50c52858a81de58f9db488aba77bc", + "css": "mic", + "code": 59435, + "src": "fontawesome" + }, + { + "uid": "598a5f2bcf3521d1615de8e1881ccd17", + "css": "clock", + "code": 59447, + "src": "fontawesome" + }, + { + "uid": "bc71f4c6e53394d5ba46b063040014f1", + "css": "cw", + "code": 59428, + "src": "fontawesome" + }, + { + "uid": "f9c3205df26e7778abac86183aefdc99", + "css": "ccw", + "code": 59427, + "src": "fontawesome" + }, + { + "uid": "a73c5deb486c8d66249811642e5d719a", + "css": "arrows-cw", + "code": 59429, + "src": "fontawesome" + }, + { + "uid": "6020aff067fc3c119cdd75daa5249220", + "css": "exchange", + "code": 59444, + "src": "fontawesome" + }, + { + "uid": "ce06b5805120d0c2f8d60cd3f1a4fdb5", + "css": "play", + "code": 59436, + "src": "fontawesome" + }, + { + "uid": "b624a1e512819d410ddbee84e6918b9d", + "css": "stop", + "code": 59437, + "src": "fontawesome" + }, + { + "uid": "0b28050bac9d3facf2f0226db643ece0", + "css": "pause", + "code": 59438, + "src": "fontawesome" + }, + { + "uid": "c47efa0e3e74f6ba4c2562c1258fff1f", + "css": "to-end", + "code": 59426, + "src": "fontawesome" + }, + { + "uid": "12052b30d23a1a70d6b32962d5464cae", + "css": "to-start", + "code": 59425, + "src": "fontawesome" + }, + { + "uid": "f9cbf7508cd04145ade2800169959eef", + "css": "font", + "code": 59419, + "src": "fontawesome" + }, + { + "uid": "02cca871bb69da75e8ee286b7055832c", + "css": "bold", + "code": 59420, + "src": "fontawesome" + }, + { + "uid": "a8cb1c217f02b073db3670c061cc54d2", + "css": "italic", + "code": 59421, + "src": "fontawesome" + }, + { + "uid": "0c708edd8fae2376b3370aa56d40cf9e", + "css": "header", + "code": 59422, + "src": "fontawesome" + }, + { + "uid": "c009d417f87d6a27bb5a1cefd30b6cbd", + "css": "text-height", + "code": 59423, + "src": "fontawesome" + }, + { + "uid": "13a971bcccd2dda26d4d4eccd8593f8a", + "css": "text-width", + "code": 59424, + "src": "fontawesome" + }, + { + "uid": "f4f0e849b805be1f6d76b65581cb3b8b", + "css": "align-left", + "code": 59392, + "src": "fontawesome" + }, + { + "uid": "ae6336c46d73af999fe7460c089abb4d", + "css": "align-center", + "code": 59407, + "src": "fontawesome" + }, + { + "uid": "e1e7306b47c3c5e6faecce9d32571381", + "css": "align-right", + "code": 59408, + "src": "fontawesome" + }, + { + "uid": "25a81737628d1e654a30ad412d7d6dd7", + "css": "align-justify", + "code": 59409, + "src": "fontawesome" + }, + { + "uid": "48b87105bd38c20315f1b705b8c7b38c", + "css": "list", + "code": 59410, + "src": "fontawesome" + }, + { + "uid": "594e9271c08ff732c04b3bf3117b9040", + "css": "indent-left", + "code": 59411, + "src": "fontawesome" + }, + { + "uid": "4d2dfc45d8176b1f26aed973fa84a91e", + "css": "indent-right", + "code": 59412, + "src": "fontawesome" + }, + { + "uid": "a2a74f5e7b7d9ba054897d8c795a326a", + "css": "list-bullet", + "code": 59413, + "src": "fontawesome" + }, + { + "uid": "f6766a8b042c2453a4e153af03294383", + "css": "list-numbered", + "code": 59414, + "src": "fontawesome" + }, + { + "uid": "61c242c9e2134d5864d7fdd57b3c9289", + "css": "strike", + "code": 59416, + "src": "fontawesome" + }, + { + "uid": "d4a4a38a40b728f46dad1de4ac950231", + "css": "underline", + "code": 59415, + "src": "fontawesome" + }, + { + "uid": "4e88371fb8857dacc1f66afe6314e426", + "css": "superscript", + "code": 59417, + "src": "fontawesome" + }, + { + "uid": "3d1c929dbc966992185ce749548c1b2c", + "css": "subscript", + "code": 59418, + "src": "fontawesome" + }, + { + "uid": "9396b2d8849e0213a0f11c5fd7fcc522", + "css": "tasks", + "code": 59442, + "src": "fontawesome" + }, + { + "uid": "0bda4bc779d4c32623dec2e43bd67ee8", + "css": "gauge", + "code": 59439, + "src": "fontawesome" + }, + { + "uid": "cda0cdcfd38f5f1d9255e722dad42012", + "css": "spinner", + "code": 59406, + "src": "fontawesome" + }, + { + "uid": "fa9a0b7e788c2d78e24cef1de6b70e80", + "css": "brush", + "code": 59440, + "src": "fontawesome" + }, + { + "uid": "be13b8c668eb18839d5d53107725f1de", + "css": "slideshare", + "code": 59441, + "src": "fontawesome" + } + ] +} \ No newline at end of file From 9b5ee93bce5c6853c234855398fa8b9ee81e06ef Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 18 Nov 2014 19:13:51 +0000 Subject: [PATCH 101/263] spinning loading icons --- src/static/css/pad.css | 34 +++++++++++++++++++++++++++++++- src/templates/admin/plugins.html | 8 ++++---- src/templates/pad.html | 4 ++-- 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/src/static/css/pad.css b/src/static/css/pad.css index f97e1b3e..d30d3303 100644 --- a/src/static/css/pad.css +++ b/src/static/css/pad.css @@ -224,6 +224,39 @@ li[data-key=showusers] > a #online_count { padding:10px; } +.loadingAnimation{ + -webkit-animation: loadingAnimation 2s infinite linear; + animation: loadingAnimation 2s infinite linear; + font-family: "fontawesome-etherpad"; + font-size:24px; + z-index:150; +} + +.loadingAnimation:before{ + content: "\e80e"; +} + +@-webkit-keyframes loadingAnimation { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} +@keyframes loadingAnimation { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} + #editorcontainerbox { position: absolute; bottom: 0; @@ -1107,5 +1140,4 @@ input[type=checkbox] { .gritter-light .gritter-title { text-shadow: none; } - /* End of gritter stuff */ diff --git a/src/templates/admin/plugins.html b/src/templates/admin/plugins.html index 75c47ad4..db90ee04 100644 --- a/src/templates/admin/plugins.html +++ b/src/templates/admin/plugins.html @@ -49,7 +49,7 @@

-

+

@@ -59,7 +59,7 @@

You haven't installed any plugins yet.

-


Fetching installed plugins...

+


Fetching installed plugins...

@@ -89,7 +89,7 @@
-

+

@@ -101,7 +101,7 @@

 

No plugins found.

-


Fetching...

+


Fetching...

diff --git a/src/templates/pad.html b/src/templates/pad.html index 83d09f88..f4107f2c 100644 --- a/src/templates/pad.html +++ b/src/templates/pad.html @@ -191,7 +191,7 @@
- +
@@ -308,7 +308,7 @@ █  
- loading.. +
From 9712f852e95db45937643fc62afbc3ab90020bd1 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 18 Nov 2014 19:16:50 +0000 Subject: [PATCH 102/263] remove connecting bar image --- src/static/js/pad.js | 42 ------------------------------------------ src/templates/pad.html | 2 +- 2 files changed, 1 insertion(+), 43 deletions(-) diff --git a/src/static/js/pad.js b/src/static/js/pad.js index e1c19998..4f5b23c7 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.js @@ -386,7 +386,6 @@ var pad = { diagnosticInfo: {}, initTime: 0, clientTimeOffset: null, - preloadedImages: false, padOptions: {}, // these don't require init; clientVars should all go through here @@ -728,19 +727,6 @@ var pad = { }, handleIsFullyConnected: function(isConnected, isInitialConnect) { - // load all images referenced from CSS, one at a time, - // starting one second after connection is first established. - if (isConnected && !pad.preloadedImages) - { - window.setTimeout(function() - { - if (!pad.preloadedImages) - { - pad.preloadImages(); - pad.preloadedImages = true; - } - }, 1000); - } pad.determineChatVisibility(isConnected && !isInitialConnect); pad.determineAuthorshipColorsVisibility(); @@ -837,34 +823,6 @@ var pad = { { pad.collabClient.addHistoricalAuthors(data); } - }, - preloadImages: function() - { - var images = ["../static/img/connectingbar.gif"]; - - function loadNextImage() - { - if (images.length == 0) - { - return; - } - var img = new Image(); - img.src = images.shift(); - if (img.complete) - { - scheduleLoadNextImage(); - } - else - { - $(img).bind('error load onreadystatechange', scheduleLoadNextImage); - } - } - - function scheduleLoadNextImage() - { - window.setTimeout(loadNextImage, 0); - } - scheduleLoadNextImage(); } }; diff --git a/src/templates/pad.html b/src/templates/pad.html index f4107f2c..aba3f64f 100644 --- a/src/templates/pad.html +++ b/src/templates/pad.html @@ -217,7 +217,7 @@

-

+

From 4a12eec3e00dd9b0ae35e9bf5044ae0c1a22643c Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 18 Nov 2014 21:10:40 +0000 Subject: [PATCH 103/263] a font lookup file --- src/static/css/fontawesome-etherpad-codes.css | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 src/static/css/fontawesome-etherpad-codes.css diff --git a/src/static/css/fontawesome-etherpad-codes.css b/src/static/css/fontawesome-etherpad-codes.css new file mode 100644 index 00000000..3bbfe6e9 --- /dev/null +++ b/src/static/css/fontawesome-etherpad-codes.css @@ -0,0 +1,57 @@ + +.icon-align-left:before { content: '\e800'; } /* '' */ +.icon-print:before { content: '\e801'; } /* '' */ +.icon-doc:before { content: '\e802'; } /* '' */ +.icon-file-pdf:before { content: '\e803'; } /* '' */ +.icon-file-word:before { content: '\e804'; } /* '' */ +.icon-file-excel:before { content: '\e805'; } /* '' */ +.icon-file-powerpoint:before { content: '\e806'; } /* '' */ +.icon-file-image:before { content: '\e807'; } /* '' */ +.icon-users:before { content: '\e808'; } /* '' */ +.icon-user:before { content: '\e809'; } /* '' */ +.icon-mail:before { content: '\e80a'; } /* '' */ +.icon-videocam:before { content: '\e80b'; } /* '' */ +.icon-picture:before { content: '\e80c'; } /* '' */ +.icon-eye-off:before { content: '\e80d'; } /* '' */ +.icon-spinner:before { content: '\e80e'; } /* '' */ +.icon-align-center:before { content: '\e80f'; } /* '' */ +.icon-align-right:before { content: '\e810'; } /* '' */ +.icon-align-justify:before { content: '\e811'; } /* '' */ +.icon-list:before { content: '\e812'; } /* '' */ +.icon-indent-left:before { content: '\e813'; } /* '' */ +.icon-indent-right:before { content: '\e814'; } /* '' */ +.icon-list-bullet:before { content: '\e815'; } /* '' */ +.icon-list-numbered:before { content: '\e816'; } /* '' */ +.icon-underline:before { content: '\e817'; } /* '' */ +.icon-strike:before { content: '\e818'; } /* '' */ +.icon-superscript:before { content: '\e819'; } /* '' */ +.icon-subscript:before { content: '\e81a'; } /* '' */ +.icon-font:before { content: '\e81b'; } /* '' */ +.icon-bold:before { content: '\e81c'; } /* '' */ +.icon-italic:before { content: '\e81d'; } /* '' */ +.icon-header:before { content: '\e81e'; } /* '' */ +.icon-text-height:before { content: '\e81f'; } /* '' */ +.icon-text-width:before { content: '\e820'; } /* '' */ +.icon-to-start:before { content: '\e821'; } /* '' */ +.icon-to-end:before { content: '\e822'; } /* '' */ +.icon-ccw:before { content: '\e823'; } /* '' */ +.icon-cw:before { content: '\e824'; } /* '' */ +.icon-arrows-cw:before { content: '\e825'; } /* '' */ +.icon-file-code:before { content: '\e826'; } /* '' */ +.icon-code:before { content: '\e827'; } /* '' */ +.icon-chat:before { content: '\e828'; } /* '' */ +.icon-comment-empty:before { content: '\e829'; } /* '' */ +.icon-trash-empty:before { content: '\e82a'; } /* '' */ +.icon-mic:before { content: '\e82b'; } /* '' */ +.icon-play:before { content: '\e82c'; } /* '' */ +.icon-stop:before { content: '\e82d'; } /* '' */ +.icon-pause:before { content: '\e82e'; } /* '' */ +.icon-gauge:before { content: '\e82f'; } /* '' */ +.icon-brush:before { content: '\e830'; } /* '' */ +.icon-slideshare:before { content: '\e831'; } /* '' */ +.icon-tasks:before { content: '\e832'; } /* '' */ +.icon-cog:before { content: '\e833'; } /* '' */ +.icon-exchange:before { content: '\e834'; } /* '' */ +.icon-star-empty:before { content: '\e835'; } /* '' */ +.icon-star:before { content: '\e836'; } /* '' */ +.icon-clock:before { content: '\e837'; } /* '' */ \ No newline at end of file From 0bea3a3af6045a4ca4b6f853d24107904a26589e Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 18 Nov 2014 21:13:21 +0000 Subject: [PATCH 104/263] no image on timeslider --- src/templates/timeslider.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/templates/timeslider.html b/src/templates/timeslider.html index 19f8dde0..fceb894f 100644 --- a/src/templates/timeslider.html +++ b/src/templates/timeslider.html @@ -116,7 +116,7 @@

-

+

From 645ec830b0f5914b209020c75c729c51629d8c08 Mon Sep 17 00:00:00 2001 From: Beau Gunderson Date: Tue, 18 Nov 2014 13:52:42 -0800 Subject: [PATCH 105/263] Don't crash on bad plugins.json; fixes #2323 --- src/static/js/pluginfw/installer.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/static/js/pluginfw/installer.js b/src/static/js/pluginfw/installer.js index bf779d7a..90bd9aa2 100644 --- a/src/static/js/pluginfw/installer.js +++ b/src/static/js/pluginfw/installer.js @@ -66,7 +66,12 @@ exports.getAvailablePlugins = function(maxCacheAge, cb) { if(exports.availablePlugins && maxCacheAge && Math.round(+new Date/1000)-cacheTimestamp <= maxCacheAge) { return cb && cb(null, exports.availablePlugins) } - plugins = JSON.parse(plugins); + try { + plugins = JSON.parse(plugins); + } catch (err) { + console.error('error parsing plugins.json:', err); + plugins = []; + } exports.availablePlugins = plugins; cacheTimestamp = Math.round(+new Date/1000); cb && cb(null, plugins) From a06f01c04a3c2f989f1d4b2465b4ee0ffb037634 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 18 Nov 2014 22:21:36 +0000 Subject: [PATCH 106/263] some timeslider styling but this wont be completed by me --- src/static/css/timeslider.css | 107 ++++++++++++++++++++-------------- 1 file changed, 62 insertions(+), 45 deletions(-) diff --git a/src/static/css/timeslider.css b/src/static/css/timeslider.css index 6608df3e..a45a7937 100644 --- a/src/static/css/timeslider.css +++ b/src/static/css/timeslider.css @@ -14,14 +14,14 @@ top: 0; } #timeslider-left { - background-image: url(../../static/img/timeslider_left.png); + background-color:#fff; height: 63px; left: 0; position: absolute; width: 134px; } #timeslider-right { - background-image: url(../../static/img/timeslider_right.png); + background-color:#fff; height: 63px; position: absolute; right: 0; @@ -29,7 +29,6 @@ width: 155px; } #timeslider { - background-image: url(../../static/img/timeslider_background.png); height: 63px; margin: 0 9px; -webkit-touch-callout: none; @@ -50,7 +49,7 @@ -webkit-user-select: none; -moz-user-select: none; user-select: none; - background-image: url(../../static/img/crushed_current_location.png); + background-color: #666; cursor: pointer; height: 61px; left: 0; @@ -63,66 +62,95 @@ -moz-user-select: none; user-select: none; cursor: pointer; - height: 35px; + height: 60px; margin-left: 5px; margin-right: 150px; position: relative; - top: 20px; + top: 0px; } + #playpause_button, #playpause_button_icon { - height: 47px; + height: 44px; position: absolute; - width: 47px; + width: 44px; + text-align:center; + vertical-align:middle; } #playpause_button { - background-image: url(../../static/img/crushed_button_undepressed.png); right: 77px; top: 9px; + height:50px; + height:50px; + background: background-linear-gradient( #F7F7F7, #F1F1F1 80%) repeat scroll 0 0 transparent; + border-radius:24px; } +#playpause_button_icon:before { + line-height:44px; + padding-left:2px; + font-family: fontawesome-etherpad; + content: "\e810"; + font-size:24px; + color:#666; +} + #playpause_button_icon { - background-image: url(../../static/img/play.png); left: 0; top: 0; + border-radius:48px; + border: solid 1px #666; } -.pause#playpause_button_icon { - background-image: url(../../static/img/pause.png) +.pause:before { + line-height:48px; + padding-left:4px; + font-family: fontawesome-etherpad; + content: "\e811"; + font-size:24px; + color:#666; } #leftstar, #rightstar, #leftstep, #rightstep { - background: url(../../static/img/stepper_buttons.png) 0 0 no-repeat; - height: 21px; + background-color: white; overflow: hidden; position: absolute; } -#leftstar { - background-position: 0 -44px; - right: 34px; - top: 8px; - width: 30px; + +.stepper{ + font-family: fontawesome-etherpad; + border-radius:2px; + border: #666 solid 1px; + line-height:18px; + text-align:center; + height:22px; + color:#666; } -#rightstar { - background-position: -29px -44px; - right: 5px; - top: 8px; - width: 29px; + +stepper:active{ + color:#000; } + #leftstep { - background-position: 0 -22px; - right: 34px; + right: 36px; top: 20px; - width: 30px; + width: 25px; } +#leftstep:before{ + content: '\e811'; + vertical-align:middle; +} +#rightstep:before{ + content: "\e812"; + vertical-align:middle; +} + #rightstep { - background-position: -29px -22px; - right: 5px; + right: 10px; top: 20px; - width: 30px; + width: 25px; } #timeslider .star { - background-image: url(../../static/img/star.png); cursor: pointer; height: 16px; position: absolute; @@ -130,7 +158,9 @@ width: 15px; } #timeslider #timer { - color: #fff; + background: linear-gradient(#F7F7F7, #F1F1F1 80%) repeat scroll 0% 0% transparent; + padding:2px; + border-radius:2px; font-family: Arial, sans-serif; font-size: 11px; left: 7px; @@ -291,16 +321,3 @@ OL { .list-number6 { list-style-type: lower-roman } -/* IE 6/7 fixes */ -* HTML #ui-slider-handle { - background-image: url(../../static/img/current_location.gif) -} -* HTML #timeslider .star { - background-image: url(../../static/img/star.gif) -} -* HTML #playpause_button_icon { - background-image: url(../../static/img/play.gif) -} -* HTML .pause#playpause_button_icon { - background-image: url(../../static/img/pause.gif) -} From e2410e41b0569c6ed71632e64e02604e1d4794a2 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 18 Nov 2014 22:24:16 +0000 Subject: [PATCH 107/263] more polish --- src/static/css/timeslider.css | 1 + src/static/js/broadcast_slider.js | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/static/css/timeslider.css b/src/static/css/timeslider.css index a45a7937..468d8f64 100644 --- a/src/static/css/timeslider.css +++ b/src/static/css/timeslider.css @@ -84,6 +84,7 @@ height:50px; background: background-linear-gradient( #F7F7F7, #F1F1F1 80%) repeat scroll 0 0 transparent; border-radius:24px; + cursor:hand; } #playpause_button_icon:before { line-height:44px; diff --git a/src/static/js/broadcast_slider.js b/src/static/js/broadcast_slider.js index 0b0147dd..822526a3 100644 --- a/src/static/js/broadcast_slider.js +++ b/src/static/js/broadcast_slider.js @@ -379,16 +379,16 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded) { var self = this; - $(self).css('background-image', 'url(/static/img/crushed_button_depressed.png)'); + // $(self).css('background-image', 'url(/static/img/crushed_button_depressed.png)'); $(self).mouseup(function(evt2) { - $(self).css('background-image', 'url(/static/img/crushed_button_undepressed.png)'); + // $(self).css('background-image', 'url(/static/img/crushed_button_undepressed.png)'); $(self).unbind('mouseup'); BroadcastSlider.playpause(); }); $(document).mouseup(function(evt2) { - $(self).css('background-image', 'url(/static/img/crushed_button_undepressed.png)'); + // $(self).css('background-image', 'url(/static/img/crushed_button_undepressed.png)'); $(document).unbind('mouseup'); }); }); From 09b08e5a72e02e93b1e54eeb09624b5c13cf3af1 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 18 Nov 2014 22:29:46 +0000 Subject: [PATCH 108/263] working pause --- src/static/css/timeslider.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/static/css/timeslider.css b/src/static/css/timeslider.css index 468d8f64..f5bbb8be 100644 --- a/src/static/css/timeslider.css +++ b/src/static/css/timeslider.css @@ -102,10 +102,10 @@ border: solid 1px #666; } .pause:before { - line-height:48px; - padding-left:4px; + line-height:44px; + padding-left:2px; font-family: fontawesome-etherpad; - content: "\e811"; + content: "\e813" !important; font-size:24px; color:#666; } From 95cb721914bc726b8f3fd660c5042ec07de3b2c3 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 18 Nov 2014 22:41:00 +0000 Subject: [PATCH 109/263] clean up --- src/static/css/fontawesome-etherpad-codes.css | 57 ------------------- 1 file changed, 57 deletions(-) delete mode 100644 src/static/css/fontawesome-etherpad-codes.css diff --git a/src/static/css/fontawesome-etherpad-codes.css b/src/static/css/fontawesome-etherpad-codes.css deleted file mode 100644 index 3bbfe6e9..00000000 --- a/src/static/css/fontawesome-etherpad-codes.css +++ /dev/null @@ -1,57 +0,0 @@ - -.icon-align-left:before { content: '\e800'; } /* '' */ -.icon-print:before { content: '\e801'; } /* '' */ -.icon-doc:before { content: '\e802'; } /* '' */ -.icon-file-pdf:before { content: '\e803'; } /* '' */ -.icon-file-word:before { content: '\e804'; } /* '' */ -.icon-file-excel:before { content: '\e805'; } /* '' */ -.icon-file-powerpoint:before { content: '\e806'; } /* '' */ -.icon-file-image:before { content: '\e807'; } /* '' */ -.icon-users:before { content: '\e808'; } /* '' */ -.icon-user:before { content: '\e809'; } /* '' */ -.icon-mail:before { content: '\e80a'; } /* '' */ -.icon-videocam:before { content: '\e80b'; } /* '' */ -.icon-picture:before { content: '\e80c'; } /* '' */ -.icon-eye-off:before { content: '\e80d'; } /* '' */ -.icon-spinner:before { content: '\e80e'; } /* '' */ -.icon-align-center:before { content: '\e80f'; } /* '' */ -.icon-align-right:before { content: '\e810'; } /* '' */ -.icon-align-justify:before { content: '\e811'; } /* '' */ -.icon-list:before { content: '\e812'; } /* '' */ -.icon-indent-left:before { content: '\e813'; } /* '' */ -.icon-indent-right:before { content: '\e814'; } /* '' */ -.icon-list-bullet:before { content: '\e815'; } /* '' */ -.icon-list-numbered:before { content: '\e816'; } /* '' */ -.icon-underline:before { content: '\e817'; } /* '' */ -.icon-strike:before { content: '\e818'; } /* '' */ -.icon-superscript:before { content: '\e819'; } /* '' */ -.icon-subscript:before { content: '\e81a'; } /* '' */ -.icon-font:before { content: '\e81b'; } /* '' */ -.icon-bold:before { content: '\e81c'; } /* '' */ -.icon-italic:before { content: '\e81d'; } /* '' */ -.icon-header:before { content: '\e81e'; } /* '' */ -.icon-text-height:before { content: '\e81f'; } /* '' */ -.icon-text-width:before { content: '\e820'; } /* '' */ -.icon-to-start:before { content: '\e821'; } /* '' */ -.icon-to-end:before { content: '\e822'; } /* '' */ -.icon-ccw:before { content: '\e823'; } /* '' */ -.icon-cw:before { content: '\e824'; } /* '' */ -.icon-arrows-cw:before { content: '\e825'; } /* '' */ -.icon-file-code:before { content: '\e826'; } /* '' */ -.icon-code:before { content: '\e827'; } /* '' */ -.icon-chat:before { content: '\e828'; } /* '' */ -.icon-comment-empty:before { content: '\e829'; } /* '' */ -.icon-trash-empty:before { content: '\e82a'; } /* '' */ -.icon-mic:before { content: '\e82b'; } /* '' */ -.icon-play:before { content: '\e82c'; } /* '' */ -.icon-stop:before { content: '\e82d'; } /* '' */ -.icon-pause:before { content: '\e82e'; } /* '' */ -.icon-gauge:before { content: '\e82f'; } /* '' */ -.icon-brush:before { content: '\e830'; } /* '' */ -.icon-slideshare:before { content: '\e831'; } /* '' */ -.icon-tasks:before { content: '\e832'; } /* '' */ -.icon-cog:before { content: '\e833'; } /* '' */ -.icon-exchange:before { content: '\e834'; } /* '' */ -.icon-star-empty:before { content: '\e835'; } /* '' */ -.icon-star:before { content: '\e836'; } /* '' */ -.icon-clock:before { content: '\e837'; } /* '' */ \ No newline at end of file From e821cb13fa0707932e9ddd2844bc7be969cf9efe Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 18 Nov 2014 22:47:20 +0000 Subject: [PATCH 110/263] more polish --- src/static/css/timeslider.css | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/static/css/timeslider.css b/src/static/css/timeslider.css index f5bbb8be..09db6dc8 100644 --- a/src/static/css/timeslider.css +++ b/src/static/css/timeslider.css @@ -108,6 +108,7 @@ content: "\e813" !important; font-size:24px; color:#666; + padding-left:0 !important; } #leftstar, #rightstar, @@ -133,7 +134,7 @@ stepper:active{ } #leftstep { - right: 36px; + right: 38px; top: 20px; width: 25px; } @@ -147,7 +148,7 @@ stepper:active{ } #rightstep { - right: 10px; + right: 12px; top: 20px; width: 25px; } From f5dcc374bae933f87ee3232d90750d9f1675e9e8 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 18 Nov 2014 23:15:56 +0000 Subject: [PATCH 111/263] slight ui fix --- src/static/js/pad_editbar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/static/js/pad_editbar.js b/src/static/js/pad_editbar.js index d39bfecd..bdf2d556 100644 --- a/src/static/js/pad_editbar.js +++ b/src/static/js/pad_editbar.js @@ -181,7 +181,7 @@ var padeditbar = (function() }, redrawHeight: function(){ var editbarHeight = $('.menu_left').height() + 2 + "px"; - var containerTop = $('.menu_left').height() + 5 + "px"; + var containerTop = $('.menu_left').height() + 7 + "px"; $('#editbar').css("height", editbarHeight); $('#editorcontainer').css("top", containerTop); }, From 0f1fd0b2818393386c13801cc83ec0cb51a1fe98 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 18 Nov 2014 23:51:22 +0000 Subject: [PATCH 112/263] working blocks --- src/templates/pad.html | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/templates/pad.html b/src/templates/pad.html index 15fd45e2..ed1332a7 100644 --- a/src/templates/pad.html +++ b/src/templates/pad.html @@ -42,7 +42,11 @@ <% e.begin_block("styles"); %> + + <% e.begin_block("customStyles"); %> + <% e.end_block(); %> + <% e.end_block(); %> @@ -347,7 +351,9 @@ + <% e.begin_block("customScripts"); %> + <% e.end_block(); %> ", 200); + res.send("", 200); }); } diff --git a/src/node/utils/tar.json b/src/node/utils/tar.json index 70001f8f..05d764a7 100644 --- a/src/node/utils/tar.json +++ b/src/node/utils/tar.json @@ -2,6 +2,7 @@ "pad.js": [ "pad.js" , "pad_utils.js" + , "browser.js" , "pad_cookie.js" , "pad_editor.js" , "pad_editbar.js" @@ -24,6 +25,7 @@ , "colorutils.js" , "draggable.js" , "pad_utils.js" + , "browser.js" , "pad_cookie.js" , "pad_editor.js" , "pad_editbar.js" @@ -42,6 +44,7 @@ ] , "ace2_inner.js": [ "ace2_inner.js" + , "browser.js" , "AttributePool.js" , "Changeset.js" , "ChangesetUtils.js" @@ -58,6 +61,7 @@ ] , "ace2_common.js": [ "ace2_common.js" + , "browser.js" , "jquery.js" , "rjquery.js" , "$async.js" diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 850d2516..3728efb2 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -28,7 +28,6 @@ $ = jQuery = require('./rjquery').$; _ = require("./underscore"); var isNodeText = Ace2Common.isNodeText, - browser = $.browser, getAssoc = Ace2Common.getAssoc, setAssoc = Ace2Common.setAssoc, isTextNode = Ace2Common.isTextNode, @@ -52,6 +51,7 @@ function Ace2Inner(){ var SkipList = require('./skiplist'); var undoModule = require('./undomodule').undoModule; var AttributeManager = require('./AttributeManager'); + var browser = require('./browser'); var DEBUG = false; //$$ build script replaces the string "var DEBUG=true;//$$" with "var DEBUG=false;" // changed to false @@ -3722,7 +3722,6 @@ function Ace2Inner(){ } if ((!specialHandled) && isTypeForCmdKey && String.fromCharCode(which).toLowerCase() == "z" && (evt.metaKey || evt.ctrlKey) && !evt.altKey) { - // cmd-Z (undo) fastIncorp(6); evt.preventDefault(); if (evt.shiftKey) @@ -3857,7 +3856,7 @@ function Ace2Inner(){ /* Attempt to apply some sanity to cursor handling in Chrome after a copy / paste event We have to do this the way we do because rep. doesn't hold the value for keyheld events IE if the user presses and holds the arrow key .. Sorry if this is ugly, blame Chrome's weird handling of viewports after new content is added*/ - if((evt.which == 37 || evt.which == 38 || evt.which == 39 || evt.which == 40) && $.browser.chrome){ + if((evt.which == 37 || evt.which == 38 || evt.which == 39 || evt.which == 40) && browser.chrome){ var viewport = getViewPortTopBottom(); var myselection = document.getSelection(); // get the current caret selection, can't use rep. here because that only gives us the start position not the current var caretOffsetTop = myselection.focusNode.parentNode.offsetTop || myselection.focusNode.offsetTop; // get the carets selection offset in px IE 214 diff --git a/src/static/js/broadcast.js b/src/static/js/broadcast.js index 9ac8ca3d..191d802e 100644 --- a/src/static/js/broadcast.js +++ b/src/static/js/broadcast.js @@ -66,7 +66,7 @@ function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro } // for IE - if ($.browser.msie) + if (browser.msie) { try { diff --git a/src/static/js/browser.js b/src/static/js/browser.js new file mode 100644 index 00000000..f763963e --- /dev/null +++ b/src/static/js/browser.js @@ -0,0 +1,240 @@ +/*! + * Bowser - a browser detector + * https://github.com/ded/bowser + * MIT License | (c) Dustin Diaz 2014 + */ + +!function (name, definition) { + if (typeof module != 'undefined' && module.exports) module.exports['browser'] = definition() + else if (typeof define == 'function' && define.amd) define(definition) + else this[name] = definition() +}('bowser', function () { + /** + * See useragents.js for examples of navigator.userAgent + */ + + var t = true + + function detect(ua) { + + function getFirstMatch(regex) { + var match = ua.match(regex); + return (match && match.length > 1 && match[1]) || ''; + } + + var iosdevice = getFirstMatch(/(ipod|iphone|ipad)/i).toLowerCase() + , likeAndroid = /like android/i.test(ua) + , android = !likeAndroid && /android/i.test(ua) + , versionIdentifier = getFirstMatch(/version\/(\d+(\.\d+)?)/i) + , tablet = /tablet/i.test(ua) + , mobile = !tablet && /[^-]mobi/i.test(ua) + , result + + if (/opera|opr/i.test(ua)) { + result = { + name: 'Opera' + , opera: t + , version: versionIdentifier || getFirstMatch(/(?:opera|opr)[\s\/](\d+(\.\d+)?)/i) + } + } + else if (/windows phone/i.test(ua)) { + result = { + name: 'Windows Phone' + , windowsphone: t + , msie: t + , version: getFirstMatch(/iemobile\/(\d+(\.\d+)?)/i) + } + } + else if (/msie|trident/i.test(ua)) { + result = { + name: 'Internet Explorer' + , msie: t + , version: getFirstMatch(/(?:msie |rv:)(\d+(\.\d+)?)/i) + } + } + else if (/chrome|crios|crmo/i.test(ua)) { + result = { + name: 'Chrome' + , chrome: t + , version: getFirstMatch(/(?:chrome|crios|crmo)\/(\d+(\.\d+)?)/i) + } + } + else if (iosdevice) { + result = { + name : iosdevice == 'iphone' ? 'iPhone' : iosdevice == 'ipad' ? 'iPad' : 'iPod' + } + // WTF: version is not part of user agent in web apps + if (versionIdentifier) { + result.version = versionIdentifier + } + } + else if (/sailfish/i.test(ua)) { + result = { + name: 'Sailfish' + , sailfish: t + , version: getFirstMatch(/sailfish\s?browser\/(\d+(\.\d+)?)/i) + } + } + else if (/seamonkey\//i.test(ua)) { + result = { + name: 'SeaMonkey' + , seamonkey: t + , version: getFirstMatch(/seamonkey\/(\d+(\.\d+)?)/i) + } + } + else if (/firefox|iceweasel/i.test(ua)) { + result = { + name: 'Firefox' + , firefox: t + , version: getFirstMatch(/(?:firefox|iceweasel)[ \/](\d+(\.\d+)?)/i) + } + if (/\((mobile|tablet);[^\)]*rv:[\d\.]+\)/i.test(ua)) { + result.firefoxos = t + } + } + else if (/silk/i.test(ua)) { + result = { + name: 'Amazon Silk' + , silk: t + , version : getFirstMatch(/silk\/(\d+(\.\d+)?)/i) + } + } + else if (android) { + result = { + name: 'Android' + , version: versionIdentifier + } + } + else if (/phantom/i.test(ua)) { + result = { + name: 'PhantomJS' + , phantom: t + , version: getFirstMatch(/phantomjs\/(\d+(\.\d+)?)/i) + } + } + else if (/blackberry|\bbb\d+/i.test(ua) || /rim\stablet/i.test(ua)) { + result = { + name: 'BlackBerry' + , blackberry: t + , version: versionIdentifier || getFirstMatch(/blackberry[\d]+\/(\d+(\.\d+)?)/i) + } + } + else if (/(web|hpw)os/i.test(ua)) { + result = { + name: 'WebOS' + , webos: t + , version: versionIdentifier || getFirstMatch(/w(?:eb)?osbrowser\/(\d+(\.\d+)?)/i) + }; + /touchpad\//i.test(ua) && (result.touchpad = t) + } + else if (/bada/i.test(ua)) { + result = { + name: 'Bada' + , bada: t + , version: getFirstMatch(/dolfin\/(\d+(\.\d+)?)/i) + }; + } + else if (/tizen/i.test(ua)) { + result = { + name: 'Tizen' + , tizen: t + , version: getFirstMatch(/(?:tizen\s?)?browser\/(\d+(\.\d+)?)/i) || versionIdentifier + }; + } + else if (/safari/i.test(ua)) { + result = { + name: 'Safari' + , safari: t + , version: versionIdentifier + } + } + else result = {} + + // set webkit or gecko flag for browsers based on these engines + if (/(apple)?webkit/i.test(ua)) { + result.name = result.name || "Webkit" + result.webkit = t + if (!result.version && versionIdentifier) { + result.version = versionIdentifier + } + } else if (!result.opera && /gecko\//i.test(ua)) { + result.name = result.name || "Gecko" + result.gecko = t + result.version = result.version || getFirstMatch(/gecko\/(\d+(\.\d+)?)/i) + } + + // set OS flags for platforms that have multiple browsers + if (android || result.silk) { + result.android = t + } else if (iosdevice) { + result[iosdevice] = t + result.ios = t + } + + // OS version extraction + var osVersion = ''; + if (iosdevice) { + osVersion = getFirstMatch(/os (\d+([_\s]\d+)*) like mac os x/i); + osVersion = osVersion.replace(/[_\s]/g, '.'); + } else if (android) { + osVersion = getFirstMatch(/android[ \/-](\d+(\.\d+)*)/i); + } else if (result.windowsphone) { + osVersion = getFirstMatch(/windows phone (?:os)?\s?(\d+(\.\d+)*)/i); + } else if (result.webos) { + osVersion = getFirstMatch(/(?:web|hpw)os\/(\d+(\.\d+)*)/i); + } else if (result.blackberry) { + osVersion = getFirstMatch(/rim\stablet\sos\s(\d+(\.\d+)*)/i); + } else if (result.bada) { + osVersion = getFirstMatch(/bada\/(\d+(\.\d+)*)/i); + } else if (result.tizen) { + osVersion = getFirstMatch(/tizen[\/\s](\d+(\.\d+)*)/i); + } + if (osVersion) { + result.osversion = osVersion; + } + + // device type extraction + var osMajorVersion = osVersion.split('.')[0]; + if (tablet || iosdevice == 'ipad' || (android && (osMajorVersion == 3 || (osMajorVersion == 4 && !mobile))) || result.silk) { + result.tablet = t + } else if (mobile || iosdevice == 'iphone' || iosdevice == 'ipod' || android || result.blackberry || result.webos || result.bada) { + result.mobile = t + } + + // Graded Browser Support + // http://developer.yahoo.com/yui/articles/gbs + if ((result.msie && result.version >= 10) || + (result.chrome && result.version >= 20) || + (result.firefox && result.version >= 20.0) || + (result.safari && result.version >= 6) || + (result.opera && result.version >= 10.0) || + (result.ios && result.osversion && result.osversion.split(".")[0] >= 6) || + (result.blackberry && result.version >= 10.1) + ) { + result.a = t; + } + else if ((result.msie && result.version < 10) || + (result.chrome && result.version < 20) || + (result.firefox && result.version < 20.0) || + (result.safari && result.version < 6) || + (result.opera && result.version < 10.0) || + (result.ios && result.osversion && result.osversion.split(".")[0] < 6) + ) { + result.c = t + } else result.x = t + + return result + } + + var bowser = detect(typeof navigator !== 'undefined' ? navigator.userAgent : '') + + + /* + * Set our detect method to the main bowser object so we can + * reuse it to test other user agents. + * This is needed to implement future tests. + */ + bowser._detect = detect; + + return bowser +}); diff --git a/src/static/js/collab_client.js b/src/static/js/collab_client.js index 146ec51b..c59c36a6 100644 --- a/src/static/js/collab_client.js +++ b/src/static/js/collab_client.js @@ -82,7 +82,7 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options, _pad) {} }; - if ($.browser.mozilla) + if (browser.mozilla) { // Prevent "escape" from taking effect and canceling a comet connection; // doesn't work if focus is on an iframe. diff --git a/src/static/js/farbtastic.js b/src/static/js/farbtastic.js index 114c4d72..8a61627d 100644 --- a/src/static/js/farbtastic.js +++ b/src/static/js/farbtastic.js @@ -170,7 +170,7 @@ $._farbtastic = function (container, options) { // New color color2 = fb.pack(fb.HSLToRGB([d2, 1, 0.5])); if (i > 0) { - if ($.browser.msie) { + if (browser.msie) { // IE's gradient calculations mess up the colors. Correct along the diagonals. var corr = (1 + Math.min(Math.abs(Math.tan(angle1)), Math.abs(Math.tan(Math.PI / 2 - angle1)))) / n; color1 = fb.pack(fb.HSLToRGB([d1 - 0.15 * corr, 1, 0.5])); @@ -254,7 +254,7 @@ $._farbtastic = function (container, options) { fb.ctxMask.drawImage(buffer, 0, 0, sz + 1, sz + 1, -sq, -sq, sq * 2, sq * 2); } // Method #2: drawing commands (old Canvas). - else if (!$.browser.msie) { + else if (!browser.msie) { // Render directly at half-resolution var sz = Math.floor(size / 2); calculateMask(sz, sz, function (x, y, c, a) { diff --git a/src/static/js/jquery_browser.js b/src/static/js/jquery_browser.js deleted file mode 100644 index 0d61e0dd..00000000 --- a/src/static/js/jquery_browser.js +++ /dev/null @@ -1,50 +0,0 @@ -/* - Copied from jQuery 1.8, the last jquery version with browser recognition support -*/ - -(function(){ - // Use of jQuery.browser is frowned upon. - // More details: http://api.jquery.com/jQuery.browser - // jQuery.uaMatch maintained for back-compat - var uaMatch = function( ua ) { - ua = ua.toLowerCase(); - - var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) || - /(webkit)[ \/]([\w.]+)/.exec( ua ) || - /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) || - /(msie) ([\w.]+)/.exec( ua ) || - ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) || - []; - - return { - browser: match[ 1 ] || "", - version: match[ 2 ] || "0" - }; - }; - - var userAgent = navigator.userAgent; - var matched = uaMatch(userAgent); - var browser = {}; - - if ( matched.browser ) { - browser[ matched.browser ] = true; - browser.version = matched.version; - } - - // Chrome is Webkit, but Webkit is also Safari. - if ( browser.chrome ) { - browser.webkit = true; - } else if ( browser.webkit ) { - browser.safari = true; - } - - //custom extensions, the original jquery didn't have these - browser.windows = /windows/i.test(userAgent); - browser.mobile = /mobile/i.test(userAgent) || /android/i.test(userAgent); - - if(typeof exports !== 'undefined'){ - exports.browser = browser; - } else{ - $.browser = browser; - } -})(); \ No newline at end of file diff --git a/src/static/js/pad.js b/src/static/js/pad.js index 4f5b23c7..78c20657 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.js @@ -43,7 +43,6 @@ var padsavedrevs = require('./pad_savedrevs'); var paduserlist = require('./pad_userlist').paduserlist; var padutils = require('./pad_utils').padutils; var colorutils = require('./colorutils').colorutils; - var createCookie = require('./pad_utils').createCookie; var readCookie = require('./pad_utils').readCookie; var randomString = require('./pad_utils').randomString; @@ -453,13 +452,13 @@ var pad = { pad.initTime = +(new Date()); pad.padOptions = clientVars.initialOptions; - if ((!$.browser.msie) && (!($.browser.mozilla && $.browser.version.indexOf("1.8.") == 0))) + if ((!browser.msie) && (!(browser.mozilla && browser.version.indexOf("1.8.") == 0))) { document.domain = document.domain; // for comet } // for IE - if ($.browser.msie) + if (browser.msie) { try { diff --git a/src/static/js/pad_userlist.js b/src/static/js/pad_userlist.js index 93c8ff70..595de92a 100644 --- a/src/static/js/pad_userlist.js +++ b/src/static/js/pad_userlist.js @@ -730,7 +730,7 @@ var paduserlist = (function() $("#myswatch").css({'background-color': myUserInfo.colorId}); - if ($.browser.msie && parseInt($.browser.version) <= 8) { + if (browser.msie && parseInt(browser.version) <= 8) { $("li[data-key=showusers] > a").css({'box-shadow': 'inset 0 0 30px ' + myUserInfo.colorId,'background-color': myUserInfo.colorId}); } else diff --git a/src/static/js/rjquery.js b/src/static/js/rjquery.js index d9d1ed16..1c0d98e6 100644 --- a/src/static/js/rjquery.js +++ b/src/static/js/rjquery.js @@ -1,10 +1,5 @@ // Proviedes a require'able version of jQuery without leaking $ and jQuery; - require('./jquery'); var jq = window.$.noConflict(true); - -//added the old browser recognition -jq.browser = require('./jquery_browser').browser; - -exports.jQuery = exports.$ = jq; \ No newline at end of file +exports.jQuery = exports.$ = jq; diff --git a/src/templates/pad.html b/src/templates/pad.html index f0267002..c20530ef 100644 --- a/src/templates/pad.html +++ b/src/templates/pad.html @@ -367,8 +367,8 @@ require.setGlobalKeyPath("require"); $ = jQuery = require('ep_etherpad-lite/static/js/rjquery').jQuery; // Expose jQuery #HACK - - if ((!$.browser.msie) && (!($.browser.mozilla && $.browser.version.indexOf("1.8.") == 0))) { + browser = require('ep_etherpad-lite/static/js/browser').browser; + if ((!browser.msie) && (!(browser.mozilla && browser.version.indexOf("1.8.") == 0))) { document.domain = document.domain; // for comet } diff --git a/src/templates/timeslider.html b/src/templates/timeslider.html index fceb894f..1fab0279 100644 --- a/src/templates/timeslider.html +++ b/src/templates/timeslider.html @@ -215,8 +215,9 @@ require.setGlobalKeyPath("require"); $ = jQuery = require('ep_etherpad-lite/static/js/rjquery').jQuery; // Expose jQuery #HACK + browser = require('ep_etherpad-lite/static/js/browser').browser; - if ((!$.browser.msie) && (!($.browser.mozilla && $.browser.version.indexOf("1.8.") == 0))) { + if ((!browser.msie) && (!(browser.mozilla && browser.version.indexOf("1.8.") == 0))) { document.domain = document.domain; // for comet } From 5f2e830b72b82ec062dca7237cc7f1796a4f32f4 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 22 Nov 2014 19:13:23 +0000 Subject: [PATCH 123/263] whoopsi --- src/static/js/ace2_inner.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 3728efb2..c9991a9a 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -3722,6 +3722,7 @@ function Ace2Inner(){ } if ((!specialHandled) && isTypeForCmdKey && String.fromCharCode(which).toLowerCase() == "z" && (evt.metaKey || evt.ctrlKey) && !evt.altKey) { + // cmd-Z (undo) fastIncorp(6); evt.preventDefault(); if (evt.shiftKey) From ae7da122d7d35e1da38280f12defd65a899859a1 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 23 Nov 2014 14:14:01 +0000 Subject: [PATCH 124/263] fix session management --- src/node/hooks/express/adminsettings.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/node/hooks/express/adminsettings.js b/src/node/hooks/express/adminsettings.js index 3529e205..42a07c73 100644 --- a/src/node/hooks/express/adminsettings.js +++ b/src/node/hooks/express/adminsettings.js @@ -23,8 +23,7 @@ exports.socketio = function (hook_name, args, cb) { var io = args.io.of("/settings"); io.on('connection', function (socket) { - console.warn ("The middleware now handles auth but I'm not convinced SocketIO is being responsible enough here so this needs reviewing before hitting master"); - // if (!socket.handshake.session || !socket.handshake.session.user || !socket.handshake.session.user.is_admin) return; + if (!socket.conn.request.session || !socket.conn.request.session.user || !socket.conn.request.session.user.is_admin) return; socket.on("load", function (query) { fs.readFile('settings.json', 'utf8', function (err,data) { From 73bcbbcb8985568d8b76af8d49443eec7d52f12e Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 23 Nov 2014 14:15:03 +0000 Subject: [PATCH 125/263] final commit --- src/node/hooks/express/adminplugins.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/node/hooks/express/adminplugins.js b/src/node/hooks/express/adminplugins.js index ccc100dc..ded1f660 100644 --- a/src/node/hooks/express/adminplugins.js +++ b/src/node/hooks/express/adminplugins.js @@ -25,8 +25,7 @@ exports.socketio = function (hook_name, args, cb) { var io = args.io.of("/pluginfw/installer"); io.on('connection', function (socket) { - console.warn ("The middleware now handles auth but I'm not convinced SocketIO is being responsible enough here so this needs reviewing before hitting master"); - // if (!socket.handshake.session || !socket.handshake.session.user || !socket.handshake.session.user.is_admin) return; + if (!socket.conn.request.session || !socket.conn.request.session.user || !socket.conn.request.session.user.is_admin) return; socket.on("getInstalled", function (query) { // send currently installed plugins From a3f6b2edb766589e014871b8d656b5e092792c72 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 23 Nov 2014 22:33:56 +0000 Subject: [PATCH 126/263] more fixes --- src/node/handler/PadMessageHandler.js | 28 ++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index 6c029325..598b2df5 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -98,7 +98,14 @@ exports.kickSessionsFromPad = function(padID) return; //skip if there is nobody on this pad - if(socketio.sockets.clients(padID).length == 0) + var roomClients = [], room = socketio.sockets.adapter.rooms[padID]; + if (room) { + for (var id in room) { + roomClients.push(socketio.sockets.adapter.nsp.connected[id]); + } + } + + if(roomClients.length == 0) return; //disconnect everyone from this pad @@ -1589,8 +1596,16 @@ function composePadChangesets(padId, startNum, endNum, callback) * Get the number of users in a pad */ exports.padUsersCount = function (padID, callback) { + + var roomClients = [], room = socketio.sockets.adapter.rooms[padID]; + if (room) { + for (var id in room) { + roomClients.push(socketio.sockets.adapter.nsp.connected[id]); + } + } + callback(null, { - padUsersCount: socketio.sockets.clients(padID).length + padUsersCount: roomClients.length }); } @@ -1600,7 +1615,14 @@ exports.padUsersCount = function (padID, callback) { exports.padUsers = function (padID, callback) { var result = []; - async.forEach(socketio.sockets.clients(padID), function(roomClient, callback) { + var roomClients = [], room = socketio.sockets.adapter.rooms[padID]; + if (room) { + for (var id in room) { + roomClients.push(socketio.sockets.adapter.nsp.connected[id]); + } + } + + async.forEach(roomClients, function(roomClient, callback) { var s = sessioninfos[roomClient.id]; if(s) { authorManager.getAuthor(s.author, function(err, author) { From 5e843a5877d72c7387a53c6ab3fba627a4f3ddba Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 25 Nov 2014 01:21:19 +0000 Subject: [PATCH 127/263] remove jshint --- bin/jshint.sh | 9 --------- 1 file changed, 9 deletions(-) delete mode 100755 bin/jshint.sh diff --git a/bin/jshint.sh b/bin/jshint.sh deleted file mode 100755 index 4dea7396..00000000 --- a/bin/jshint.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh - -if [ -d "../bin" ]; then - cd "../" -fi - -JSHINT=./node_modules/jshint/bin/hint - -$JSHINT ./node/ From 97068b562dce9525fda9fb7a08f54faf9ef2837d Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 25 Nov 2014 01:22:32 +0000 Subject: [PATCH 128/263] more old load testing cruft --- bin/loadTesting/README | 79 ------------------------------------- bin/loadTesting/launcher.sh | 16 -------- bin/loadTesting/loader.js | 20 ---------- 3 files changed, 115 deletions(-) delete mode 100644 bin/loadTesting/README delete mode 100755 bin/loadTesting/launcher.sh delete mode 100644 bin/loadTesting/loader.js diff --git a/bin/loadTesting/README b/bin/loadTesting/README deleted file mode 100644 index c8ecd71e..00000000 --- a/bin/loadTesting/README +++ /dev/null @@ -1,79 +0,0 @@ -This is the new load testing file: https://bitbucket.org/rbraakman/etherpad-stresstest - -BELOW is the original load testing file. - -This load tester is extremely useful for testing how many dormant clients can connect to etherpad. - -TODO: -Emulate characters being typed into a pad - -HOW TO USE (from @mjd75) proper formatting at: https://github.com/ether/etherpad-lite/issues/360 - -Server 1: -Installed Node.js (etc), EtherPad and MySQL - -Server 2: -Installed Xvfb and PhantomJS - -I installed Xvfb following (roughly) this guide: http://blog.martin-lyness.com/archives/installing-xvfb-on-ubuntu-9-10-karmic-koala - - #sudo apt-get install xvfb - #sudo apt-get install xfonts-100dpi xfonts-75dpi xfonts-scalable xfonts-cyrillic - -Launched two instances of Xvfb directly from the terminal: - - #Xvfb :0 -ac - #Xvfb :1 -ac - -I installed PhantomJS following this guide: http://code.google.com/p/phantomjs/wiki/Installation - - #sudo add-apt-repository ppa:jerome-etienne/neoip - #sudo apt-get update - #sudo apt-get install phantomjs - -I created a small JavaScript file for PhatomJS to use to control the browser instances: - -### BEGIN JAVASCRIPT ### - -var page = new WebPage(), - t, address; - -if (phantom.args.length === 0) { - console.log('Usage: loader.js '); - phantom.exit(); -} else { - t = Date.now(); - address = phantom.args[0]; - - var page = new WebPage(); - page.onResourceRequested = function (request) { - console.log('Request ' + JSON.stringify(request, undefined, 4)); - }; - page.onResourceReceived = function (response) { - console.log('Receive ' + JSON.stringify(response, undefined, 4)); - }; - page.open(address); - -} - -### END JAVASCRIPT ### - -And finally a launcher script that uses screen to run 400 instances of PhantomJS with the above script: - -### BEGIN SHELL SCRIPT ### - -#!/bin/bash - -# connect 200 instances to display :0 -for i in {1..200} -do - DISPLAY=:0 screen -d -m phantomjs loader.js http://ec2-50-17-168-xx.compute-1.amazonaws.com:9001/p/pad2 && sleep 2 -done - -# connect 200 instances to display :1 -for i in {1..200} -do - DISPLAY=:1 screen -d -m phantomjs loader.js http://ec2-50-17-168-xx.compute-1.amazonaws.com:9001/p/pad2 && sleep 2 -done - -### END SHELL SCRIPT ### diff --git a/bin/loadTesting/launcher.sh b/bin/loadTesting/launcher.sh deleted file mode 100755 index e940f8e0..00000000 --- a/bin/loadTesting/launcher.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -# connect 500 instances to display :0 -for i in {1..500} -do - echo $i - echo "Displaying Some shit" - DISPLAY=:0 screen -d -m /home/phantomjs/bin/phantomjs loader.js http://10.0.0.55:9001/p/pad2 && sleep 2 -done - -# connect 500 instances to display :1 -for i in {1..500} -do - echo $i - DISPLAY=:1 screen -d -m /home/phantomjs/bin/phantomjs loader.js http://10.0.0.55:9001/p/pad2 && sleep 2 -done diff --git a/bin/loadTesting/loader.js b/bin/loadTesting/loader.js deleted file mode 100644 index ddcd0572..00000000 --- a/bin/loadTesting/loader.js +++ /dev/null @@ -1,20 +0,0 @@ -var page = new WebPage(), - t, address; - -if (phantom.args.length === 0) { - console.log('Usage: loader.js '); - phantom.exit(); -} else { - t = Date.now(); - address = phantom.args[0]; - - var page = new WebPage(); - page.onResourceRequested = function (request) { - console.log('Request ' + JSON.stringify(request, undefined, 4)); - }; - page.onResourceReceived = function (response) { - console.log('Receive ' + JSON.stringify(response, undefined, 4)); - }; - page.open(address); - -} From 60d44cd3df201088538d561795dee56badbcc6b2 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 25 Nov 2014 17:26:09 +0000 Subject: [PATCH 129/263] use cheerio instead of jsdom --- src/node/utils/ImportHtml.js | 14 +++++--------- src/package.json | 2 +- src/static/js/contentcollector.js | 5 +++++ 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/node/utils/ImportHtml.js b/src/node/utils/ImportHtml.js index 48188dfd..79217cb6 100644 --- a/src/node/utils/ImportHtml.js +++ b/src/node/utils/ImportHtml.js @@ -14,22 +14,18 @@ * limitations under the License. */ -var jsdom = require('jsdom-nocontextifiy').jsdom; +var jsdom = require('jsdom').jsdom; var log4js = require('log4js'); var Changeset = require("ep_etherpad-lite/static/js/Changeset"); var contentcollector = require("ep_etherpad-lite/static/js/contentcollector"); +var cheerio = require("cheerio"); function setPadHTML(pad, html, callback) { var apiLogger = log4js.getLogger("ImportHtml"); - // Parse the incoming HTML with jsdom - try{ - var doc = jsdom(html.replace(/>\n+<')); - }catch(e){ - apiLogger.warn("Error importing, possibly caused by malformed HTML"); - var doc = jsdom("
Error during import, possibly malformed HTML
"); - } + var $ = cheerio.load(html); + var doc = $('html')[0]; apiLogger.debug('html:'); apiLogger.debug(html); @@ -38,7 +34,7 @@ function setPadHTML(pad, html, callback) // using the content collector object var cc = contentcollector.makeContentCollector(true, null, pad.pool); try{ // we use a try here because if the HTML is bad it will blow up - cc.collectContent(doc.childNodes[0]); + cc.collectContent(doc); }catch(e){ apiLogger.warn("HTML was not properly formed", e); return; // We don't process the HTML because it was bad.. diff --git a/src/package.json b/src/package.json index 4b0ae82b..e69d9730 100644 --- a/src/package.json +++ b/src/package.json @@ -25,7 +25,7 @@ "formidable" : "1.0.9", "log4js" : "0.6.6", "nodemailer" : "0.3.x", - "jsdom-nocontextifiy" : "0.2.10", + "cheerio" : "0.18.0", "async-stacktrace" : "0.0.2", "npm" : "1.4.x", "ejs" : "0.6.1", diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index 5e393670..048ed491 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -54,10 +54,14 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class }, nodeNumChildren: function(n) { + if(n.childNodes == null) return 0; return n.childNodes.length; }, nodeChild: function(n, i) { + if(n.childNodes.item == null){ + return n.childNodes[i]; + } return n.childNodes.item(i); }, nodeProp: function(n, p) @@ -66,6 +70,7 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class }, nodeAttr: function(n, a) { + if(n.getAttribute == null) return null; return n.getAttribute(a); }, optNodeInnerHTML: function(n) From c2c0cb690763d1d0752f28ef4bca6738d319c66a Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 25 Nov 2014 18:07:34 +0000 Subject: [PATCH 130/263] so yeah, prolly can do this anyway? --- src/node/handler/ImportHandler.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/node/handler/ImportHandler.js b/src/node/handler/ImportHandler.js index 8c410ecd..22d7a761 100644 --- a/src/node/handler/ImportHandler.js +++ b/src/node/handler/ImportHandler.js @@ -234,7 +234,8 @@ exports.doImport = function(req, res, padId) ERR(err); //close the connection - res.send("", 200); + // res.send("", 200); + res.send("", 200); }); } From 2a062f8dc361de3845c4f5f33209047ed9ff985e Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 25 Nov 2014 18:18:42 +0000 Subject: [PATCH 131/263] mheh no browser requirement at all --- src/node/handler/ImportHandler.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/node/handler/ImportHandler.js b/src/node/handler/ImportHandler.js index 22d7a761..5cef0829 100644 --- a/src/node/handler/ImportHandler.js +++ b/src/node/handler/ImportHandler.js @@ -234,8 +234,8 @@ exports.doImport = function(req, res, padId) ERR(err); //close the connection - // res.send("", 200); - res.send("", 200); + res.send("", 200); + }); } From 5c3874c0a1305b57fbf301850de12255cec2a0a0 Mon Sep 17 00:00:00 2001 From: luto Date: Tue, 25 Nov 2014 22:38:22 +0100 Subject: [PATCH 132/263] really recreate socketio-client in expressCreateServer, fixes #2342 When using plugins, the express server gets restarted. When we do that, the socketio-server should also get restarted. It doesn't. That means that all the events in SocketIORouter.js are bound twice, which causes chaos all over etherpad. This changes our socketio.js so it fully recreates the io-instance when we restart the server. introduced in 95e7b0f15609fc850b71717a672abd02237a0f33, but catching that would have been hard. --- src/node/hooks/express/socketio.js | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/node/hooks/express/socketio.js b/src/node/hooks/express/socketio.js index e88a3f4c..adf15212 100644 --- a/src/node/hooks/express/socketio.js +++ b/src/node/hooks/express/socketio.js @@ -1,24 +1,22 @@ var log4js = require('log4js'); var settings = require('../../utils/Settings'); +var socketio = require('socket.io'); var socketIORouter = require("../../handler/SocketIORouter"); var hooks = require("ep_etherpad-lite/static/js/pluginfw/hooks"); var webaccess = require("ep_etherpad-lite/node/hooks/express/webaccess"); -// there shouldn't be a browser that isn't compatible to all -// transports in this list at once -// e.g. XHR is disabled in IE by default, so in IE it should use jsonp-polling - -var socketio = require('socket.io')({ - transports: settings.socketTransportProtocols -}); - var padMessageHandler = require("../../handler/PadMessageHandler"); var connect = require('connect'); exports.expressCreateServer = function (hook_name, args, cb) { //init socket.io and redirect all requests to the MessageHandler - var io = socketio.listen(args.server); + // there shouldn't be a browser that isn't compatible to all + // transports in this list at once + // e.g. XHR is disabled in IE by default, so in IE it should use jsonp-polling + var io = socketio({ + transports: settings.socketTransportProtocols + }).listen(args.server); /* Require an express session cookie to be present, and load the * session. See http://www.danielbaulig.de/socket-ioexpress for more From ad7de8277d75d6029d81d0039e40f80c9fb796e2 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 25 Nov 2014 22:12:25 +0000 Subject: [PATCH 133/263] mocha --- src/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/package.json b/src/package.json index 4b0ae82b..4a472be0 100644 --- a/src/package.json +++ b/src/package.json @@ -40,7 +40,8 @@ "swagger-node-express" : ">=2.1.0", "channels" : "0.0.x", "jsonminify" : "0.2.2", - "measured" : "0.1.3" + "measured" : "0.1.3", + "mocha" : "*" }, "bin": { "etherpad-lite": "./node/server.js" }, "devDependencies": { From 77de2d918bbd61fe8be3d02caa3a62bf9a00733f Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 25 Nov 2014 22:14:48 +0000 Subject: [PATCH 134/263] multiline --- src/node/handler/ImportHandler.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/node/handler/ImportHandler.js b/src/node/handler/ImportHandler.js index 5cef0829..55915d76 100644 --- a/src/node/handler/ImportHandler.js +++ b/src/node/handler/ImportHandler.js @@ -234,8 +234,19 @@ exports.doImport = function(req, res, padId) ERR(err); //close the connection - res.send("", 200); - + res.send( + " \ + \ + \ + " + , 200); }); } From 881763f663e4f9cfa1356e113b02dbeff777451f Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 25 Nov 2014 22:15:29 +0000 Subject: [PATCH 135/263] remove jsdom dep --- src/node/utils/ImportHtml.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/node/utils/ImportHtml.js b/src/node/utils/ImportHtml.js index 79217cb6..34e9ad76 100644 --- a/src/node/utils/ImportHtml.js +++ b/src/node/utils/ImportHtml.js @@ -14,7 +14,6 @@ * limitations under the License. */ -var jsdom = require('jsdom').jsdom; var log4js = require('log4js'); var Changeset = require("ep_etherpad-lite/static/js/Changeset"); var contentcollector = require("ep_etherpad-lite/static/js/contentcollector"); From 3ac833d455dcf280e69c04a54cf7acd2e35dd91a Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 25 Nov 2014 22:47:22 +0000 Subject: [PATCH 136/263] basic test runner --- bin/backendTests.sh | 1 + tests/backend/specs/api.js | 41 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100755 bin/backendTests.sh create mode 100644 tests/backend/specs/api.js diff --git a/bin/backendTests.sh b/bin/backendTests.sh new file mode 100755 index 00000000..ab07e012 --- /dev/null +++ b/bin/backendTests.sh @@ -0,0 +1 @@ +src/node_modules/mocha/bin/mocha --timeout 5000 --reporter nyan tests/backend/specs diff --git a/tests/backend/specs/api.js b/tests/backend/specs/api.js new file mode 100644 index 00000000..5cd567c6 --- /dev/null +++ b/tests/backend/specs/api.js @@ -0,0 +1,41 @@ +var assert = require("assert") + supertest = require('supertest'), + api = supertest('http://localhost:9001'); + +describe('Array', function(){ + describe('#indexOf()', function(){ + it('should return -1 when the value is not present', function(){ + assert.equal(-1, [1,2,3].indexOf(5)); + assert.equal(-1, [1,2,3].indexOf(0)); + }) + }) +}) + +describe('Connectivity', function(){ + it('errors if can not connect', function(done) { + api.get('/api/') + .expect(200, done) + }); +}) + + + +/* +describe('Authentication', function() { + + it('errors if wrong basic auth', function(done) { + api.get('/blog') + .set('x-api-key', '123myapikey') + .auth('incorrect', 'credentials') + .expect(401, done) + }); + + it('errors if bad x-api-key header', function(done) { + api.get('/blog') + .auth('correct', 'credentials') + .expect(401) + .expect({error:"Bad or missing app identification header"}, done); + }); + +}); +*/ From a4be5b4fd7bef7d1e5d48ae715aae7dc8e359221 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 26 Nov 2014 01:03:57 +0000 Subject: [PATCH 137/263] mocha version --- src/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/package.json b/src/package.json index 4a472be0..5f684d0e 100644 --- a/src/package.json +++ b/src/package.json @@ -41,7 +41,7 @@ "channels" : "0.0.x", "jsonminify" : "0.2.2", "measured" : "0.1.3", - "mocha" : "*" + "mocha" : ">=2.0.1" }, "bin": { "etherpad-lite": "./node/server.js" }, "devDependencies": { From 7a4a3b5ef312327d8f16503a74911c60df3f2f09 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 26 Nov 2014 01:11:42 +0000 Subject: [PATCH 138/263] make internal note of which endpoints need testing --- tests/backend/specs/api.js | 122 +++++++++++++++++++++++++++++-------- 1 file changed, 97 insertions(+), 25 deletions(-) diff --git a/tests/backend/specs/api.js b/tests/backend/specs/api.js index 5cd567c6..685714c1 100644 --- a/tests/backend/specs/api.js +++ b/tests/backend/specs/api.js @@ -1,41 +1,113 @@ -var assert = require("assert") +var assert = require('assert') supertest = require('supertest'), + fs = require('fs'), api = supertest('http://localhost:9001'); + path = require('path'); -describe('Array', function(){ - describe('#indexOf()', function(){ - it('should return -1 when the value is not present', function(){ - assert.equal(-1, [1,2,3].indexOf(5)); - assert.equal(-1, [1,2,3].indexOf(0)); - }) - }) -}) +var filePath = path.join(__dirname, '../../../APIKEY.txt'); + +var apiKey = fs.readFileSync(filePath, {encoding: 'utf-8'}); +var apiVersion = 1; +var testPadId = makeid(); describe('Connectivity', function(){ it('errors if can not connect', function(done) { api.get('/api/') + .expect('Content-Type', /json/) .expect(200, done) }); }) +describe('API Versioning', function(){ + it('errors if can not connect', function(done) { + api.get('/api/') + .expect(function(res){ + apiVersion = res.body.currentVersion; + if (!res.body.currentVersion) throw new Error("No version set in API"); + return; + }) + .expect(200, done) + }); +}) + +describe('Permission', function(){ + it('errors if can connect without correct APIKey', function(done) { + // This is broken because Etherpad doesn't handle HTTP codes properly see #2343 + // If your APIKey is password you deserve to fail all tests anyway + throw new Error("Erroring anyway just because the API seems broken here"); + api.get('/api/'+apiVersion+'/createPad&apikey=password&padID=test') + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('createPad', function(){ + it('creates a new pad', function(done) { + api.get(endPoint('createPad')+"&padID="+testPadId) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +/* Endpoints to interact with.. +createPad(padID [, text]) +getRevisions(padID) +padUsersCount(padID) +deletePad(padID) +getReadOnlyID(padID) +setPublicStatus(padID, publicStatus) +getPublicStatus(padID) +setPassword(padID, password) +isPasswordProtected(padID) +listAuthorsOfPad(padID) +getLastEdited(padID) +getHTML(padID, [rev]) +setText(padID, text) +getText(padID, [rev]) + +listSessionsOfGroup(groupID) +getSessionInfo(sessionID) +deleteSession(sessionID) +createSession(groupID, authorID, validUntil) + +listPadsOfAuthor(authorID) +createAuthorIfNotExistsFor(authorMapper [, name]) +createAuthor([name]) + +createGroupPad(groupID, padName [, text]) +listPads(groupID) +deleteGroup(groupID) +createGroupIfNotExistsFor(groupMapper) +createGroup() +*/ /* -describe('Authentication', function() { - - it('errors if wrong basic auth', function(done) { - api.get('/blog') - .set('x-api-key', '123myapikey') - .auth('incorrect', 'credentials') - .expect(401, done) +describe('getRevisionsCount', function(){ + it('gets the revision counts of a new pad', function(done) { + // This is broken because Etherpad doesn't handle HTTP codes properly see #2$ + // If your APIKey is password you deserve to fail all tests anyway + api.get(endPoint('getRevisionsCount')+"&padID="+testPadId) + .expect('Content-Type', /json/) + .expect(function(res){ + console.log(res.body); + }) + .expect(200, done) }); - - it('errors if bad x-api-key header', function(done) { - api.get('/blog') - .auth('correct', 'credentials') - .expect(401) - .expect({error:"Bad or missing app identification header"}, done); - }); - -}); +}) */ + +var endPoint = function(point){ + return '/api/'+apiVersion+'/'+point+'?apikey='+apiKey; +} + +function makeid() +{ + var text = ""; + var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + + for( var i=0; i < 5; i++ ){ + text += possible.charAt(Math.floor(Math.random() * possible.length)); + } + return text; +} From 0a84379364c76a43fbab4c48af08346ff54a179d Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 26 Nov 2014 15:19:22 +0000 Subject: [PATCH 139/263] make additional line break at end of imported doc --- bin/installDeps.sh | 2 +- bin/run.sh | 2 +- src/node/utils/ImportHtml.js | 6 +++++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/bin/installDeps.sh b/bin/installDeps.sh index a8bc88a8..58eb32f2 100755 --- a/bin/installDeps.sh +++ b/bin/installDeps.sh @@ -99,7 +99,7 @@ fi echo "Clear minfified cache..." rm -f var/minified* -echo "ensure custom css/js files are created..." +echo "Ensure custom css/js files are created..." for f in "index" "pad" "timeslider" do diff --git a/bin/run.sh b/bin/run.sh index 92ae8d48..f2162310 100755 --- a/bin/run.sh +++ b/bin/run.sh @@ -32,7 +32,7 @@ fi bin/installDeps.sh $* || exit 1 #Move to the node folder and start -echo "start..." +echo "Started Etherpad..." SCRIPTPATH=`pwd -P` node $SCRIPTPATH/node_modules/ep_etherpad-lite/node/server.js $* diff --git a/src/node/utils/ImportHtml.js b/src/node/utils/ImportHtml.js index 34e9ad76..59802f9b 100644 --- a/src/node/utils/ImportHtml.js +++ b/src/node/utils/ImportHtml.js @@ -24,8 +24,12 @@ function setPadHTML(pad, html, callback) var apiLogger = log4js.getLogger("ImportHtml"); var $ = cheerio.load(html); - var doc = $('html')[0]; + // Appends a line break, used by Etherpad to ensure a caret is available + // below the last line of an import + $('body').append("

"); + + var doc = $('html')[0]; apiLogger.debug('html:'); apiLogger.debug(html); From 16f3ebb3ba4066ffb2856e378010e5ea6d843b8d Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 26 Nov 2014 17:34:44 +0000 Subject: [PATCH 140/263] handle auth fails --- src/node/handler/APIHandler.js | 1 + tests/backend/specs/api.js | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/node/handler/APIHandler.js b/src/node/handler/APIHandler.js index 9adc2418..a26dd2cf 100644 --- a/src/node/handler/APIHandler.js +++ b/src/node/handler/APIHandler.js @@ -450,6 +450,7 @@ exports.handle = function(apiVersion, functionName, fields, req, res) if(fields["apikey"] != apikey.trim()) { + res.statusCode = 401; res.send({code: 4, message: "no or wrong API Key", data: null}); return; } diff --git a/tests/backend/specs/api.js b/tests/backend/specs/api.js index 685714c1..40aa2183 100644 --- a/tests/backend/specs/api.js +++ b/tests/backend/specs/api.js @@ -34,10 +34,9 @@ describe('Permission', function(){ it('errors if can connect without correct APIKey', function(done) { // This is broken because Etherpad doesn't handle HTTP codes properly see #2343 // If your APIKey is password you deserve to fail all tests anyway - throw new Error("Erroring anyway just because the API seems broken here"); - api.get('/api/'+apiVersion+'/createPad&apikey=password&padID=test') - .expect('Content-Type', /json/) - .expect(200, done) + var permErrorURL = '/api/'+apiVersion+'/createPad?apikey=password&padID=test'; + api.get(permErrorURL) + .expect(401, done) }); }) From 1347a814f04d611499d7ee0c3c41f222d46f4ff9 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 26 Nov 2014 17:53:31 +0000 Subject: [PATCH 141/263] basic pad tests without test logic yet --- tests/backend/specs/api.js | 136 +++++++++++++++++++++++++++++++++---- 1 file changed, 124 insertions(+), 12 deletions(-) diff --git a/tests/backend/specs/api.js b/tests/backend/specs/api.js index 40aa2183..d485c900 100644 --- a/tests/backend/specs/api.js +++ b/tests/backend/specs/api.js @@ -40,39 +40,151 @@ describe('Permission', function(){ }); }) +/* Pad Tests Order of execution +-> deletePad -- This gives us a guaranteed clear environment + -> createPad + -> getRevisions(0) -- Should be 0 + -> getHTML -- Should be the default pad text in HTML format + -> deletePad -- Should just delete a pad + -> getHTML -- Should return an error + -> createPad(withText) + -> getText -- Should have the text specified above as the pad text + -> setText + -> getText -- Should be the text set before + -> getRevisions -- Should be 0 still? + -> padUsersCount -- Should be 0 + -> getReadOnlyId -- Should be a value +*/ + +describe('deletePad', function(){ + it('deletes a Pad', function(done) { + api.get(endPoint('deletePad')+"&padID="+testPadId) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + describe('createPad', function(){ - it('creates a new pad', function(done) { + it('creates a new Pad', function(done) { api.get(endPoint('createPad')+"&padID="+testPadId) .expect('Content-Type', /json/) .expect(200, done) }); }) -/* Endpoints to interact with.. -createPad(padID [, text]) -getRevisions(padID) +describe('getRevisions', function(){ + it('gets revision count of Pad', function(done) { + api.get(endPoint('getRevisions')+"&padID="+testPadId) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('getHTML', function(){ + it('get the HTML of Pad', function(done) { + api.get(endPoint('getHTML')+"&padID="+testPadId) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('deletePad', function(){ + it('deletes a Pad', function(done) { + api.get(endPoint('deletePad')+"&padID="+testPadId) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('getHTML', function(){ + it('get the HTML of a Pad', function(done) { + api.get(endPoint('getHTML')+"&padID="+testPadId) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('createPad', function(){ + it('creates a new Pad with text', function(done) { + api.get(endPoint('createPad')+"&padID="+testPadId+"&test=testText") + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('getText', function(){ + it('gets the Pad text', function(done) { + api.get(endPoint('getText')+"&padID="+testPadId) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('setText', function(){ + it('creates a new Pad with text', function(done) { + api.get(endPoint('createPad')+"&padID="+testPadId+"&test=testText") + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('getText', function(){ + it('gets the Pad text', function(done) { + api.get(endPoint('getText')+"&padID="+testPadId) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('getRevisions', function(){ + it('gets Revision Coutn of a Pad', function(done) { + api.get(endPoint('getRevisions')+"&padID="+testPadId) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('padUsersCount', function(){ + it('gets Revision Coutn of a Pad', function(done) { + api.get(endPoint('padUsersCount')+"&padID="+testPadId) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('getReadOnlyId', function(){ + it('Gets the Read Only ID of a Pad', function(done) { + api.get(endPoint('getReadOnlyId')+"&padID="+testPadId) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + + + +/* Endpoints Still to interact with.. +/ createPad(padID [, text]) +/ getRevisions(padID) +/ deletePad(padID) +/ getReadOnlyID(padID) +/ getHTML(padID, [rev]) +/ setText(padID, text) +/ getText(padID, [rev]) + padUsersCount(padID) -deletePad(padID) -getReadOnlyID(padID) setPublicStatus(padID, publicStatus) getPublicStatus(padID) setPassword(padID, password) isPasswordProtected(padID) listAuthorsOfPad(padID) getLastEdited(padID) -getHTML(padID, [rev]) -setText(padID, text) -getText(padID, [rev]) - listSessionsOfGroup(groupID) getSessionInfo(sessionID) deleteSession(sessionID) createSession(groupID, authorID, validUntil) - listPadsOfAuthor(authorID) createAuthorIfNotExistsFor(authorMapper [, name]) createAuthor([name]) - createGroupPad(groupID, padName [, text]) listPads(groupID) deleteGroup(groupID) From c0679980bf383ce452431feb7020c91d6c4ee43b Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 26 Nov 2014 19:25:09 +0000 Subject: [PATCH 142/263] all pad tests with content --- tests/backend/specs/api.js | 65 +++++++++++++++++++++++++++----------- 1 file changed, 47 insertions(+), 18 deletions(-) diff --git a/tests/backend/specs/api.js b/tests/backend/specs/api.js index d485c900..279f3088 100644 --- a/tests/backend/specs/api.js +++ b/tests/backend/specs/api.js @@ -67,14 +67,21 @@ describe('deletePad', function(){ describe('createPad', function(){ it('creates a new Pad', function(done) { api.get(endPoint('createPad')+"&padID="+testPadId) + .expect(function(res){ + if(res.body.code !== 0) throw new Error("Unable to create new Pad"); + }) .expect('Content-Type', /json/) .expect(200, done) }); }) -describe('getRevisions', function(){ +describe('getRevisionsCount', function(){ it('gets revision count of Pad', function(done) { - api.get(endPoint('getRevisions')+"&padID="+testPadId) + api.get(endPoint('getRevisionsCount')+"&padID="+testPadId) + .expect(function(res){ + if(res.body.code !== 0) throw new Error("Unable to get Revision Count"); + if(res.body.data.revisions !== 0) throw new Error("Incorrect Revision Count"); + }) .expect('Content-Type', /json/) .expect(200, done) }); @@ -83,6 +90,9 @@ describe('getRevisions', function(){ describe('getHTML', function(){ it('get the HTML of Pad', function(done) { api.get(endPoint('getHTML')+"&padID="+testPadId) + .expect(function(res){ + if(res.body.data.html.length <= 1) throw new Error("Unable to get Revision Count"); + }) .expect('Content-Type', /json/) .expect(200, done) }); @@ -91,14 +101,20 @@ describe('getHTML', function(){ describe('deletePad', function(){ it('deletes a Pad', function(done) { api.get(endPoint('deletePad')+"&padID="+testPadId) + .expect(function(res){ + if(res.body.code !== 0) throw new Error("Pad Deletion failed") + }) .expect('Content-Type', /json/) .expect(200, done) }); }) describe('getHTML', function(){ - it('get the HTML of a Pad', function(done) { + it('get the HTML of a Pad -- Should return a failure', function(done) { api.get(endPoint('getHTML')+"&padID="+testPadId) + .expect(function(res){ + if(res.body.code !== 1) throw new Error("Pad deletion failed") + }) .expect('Content-Type', /json/) .expect(200, done) }); @@ -106,15 +122,21 @@ describe('getHTML', function(){ describe('createPad', function(){ it('creates a new Pad with text', function(done) { - api.get(endPoint('createPad')+"&padID="+testPadId+"&test=testText") + api.get(endPoint('createPad')+"&padID="+testPadId+"&text=testText") + .expect(function(res){ + if(res.body.code !== 0) throw new Error("Pad Creation failed") + }) .expect('Content-Type', /json/) .expect(200, done) }); }) describe('getText', function(){ - it('gets the Pad text', function(done) { + it('gets the Pad text and expect it to be testText with \n which is a line break', function(done) { api.get(endPoint('getText')+"&padID="+testPadId) + .expect(function(res){ + if(res.body.data.text !== "testText\n") throw new Error("Pad Creation with text") + }) .expect('Content-Type', /json/) .expect(200, done) }); @@ -122,7 +144,10 @@ describe('getText', function(){ describe('setText', function(){ it('creates a new Pad with text', function(done) { - api.get(endPoint('createPad')+"&padID="+testPadId+"&test=testText") + api.get(endPoint('setText')+"&padID="+testPadId+"&text=testTextTwo") + .expect(function(res){ + if(res.body.code !== 0) throw new Error("Pad setting text failed"); + }) .expect('Content-Type', /json/) .expect(200, done) }); @@ -131,14 +156,20 @@ describe('setText', function(){ describe('getText', function(){ it('gets the Pad text', function(done) { api.get(endPoint('getText')+"&padID="+testPadId) + .expect(function(res){ + if(res.body.data.text !== "testTextTwo\n") throw new Error("Setting Text") + }) .expect('Content-Type', /json/) .expect(200, done) }); }) -describe('getRevisions', function(){ +describe('getRevisionsCount', function(){ it('gets Revision Coutn of a Pad', function(done) { - api.get(endPoint('getRevisions')+"&padID="+testPadId) + api.get(endPoint('getRevisionsCount')+"&padID="+testPadId) + .expect(function(res){ + if(res.body.data.revisions !== 1) throw new Error("Unable to set text revision count") + }) .expect('Content-Type', /json/) .expect(200, done) }); @@ -147,14 +178,20 @@ describe('getRevisions', function(){ describe('padUsersCount', function(){ it('gets Revision Coutn of a Pad', function(done) { api.get(endPoint('padUsersCount')+"&padID="+testPadId) + .expect(function(res){ + if(res.body.data.padUsersCount !== 0) throw new Error("Incorrect Pad User count") + }) .expect('Content-Type', /json/) .expect(200, done) }); }) -describe('getReadOnlyId', function(){ +describe('getReadOnlyID', function(){ it('Gets the Read Only ID of a Pad', function(done) { - api.get(endPoint('getReadOnlyId')+"&padID="+testPadId) + api.get(endPoint('getReadOnlyID')+"&padID="+testPadId) + .expect(function(res){ + if(!res.body.data.readOnlyID) throw new Error("No Read Only ID for Pad") + }) .expect('Content-Type', /json/) .expect(200, done) }); @@ -163,14 +200,6 @@ describe('getReadOnlyId', function(){ /* Endpoints Still to interact with.. -/ createPad(padID [, text]) -/ getRevisions(padID) -/ deletePad(padID) -/ getReadOnlyID(padID) -/ getHTML(padID, [rev]) -/ setText(padID, text) -/ getText(padID, [rev]) - padUsersCount(padID) setPublicStatus(padID, publicStatus) getPublicStatus(padID) From 5434d2118d9b6828f3c679f88618ee8403fd6e8b Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 26 Nov 2014 19:28:49 +0000 Subject: [PATCH 143/263] tidy up and re-organize --- bin/backendTests.sh | 2 +- tests/backend/specs/{api.js => api/pad.js} | 42 +------------------- tests/backend/specs/api/sessionsAndGroups.js | 38 ++++++++++++++++++ 3 files changed, 40 insertions(+), 42 deletions(-) rename tests/backend/specs/{api.js => api/pad.js} (85%) create mode 100644 tests/backend/specs/api/sessionsAndGroups.js diff --git a/bin/backendTests.sh b/bin/backendTests.sh index ab07e012..ec12775b 100755 --- a/bin/backendTests.sh +++ b/bin/backendTests.sh @@ -1 +1 @@ -src/node_modules/mocha/bin/mocha --timeout 5000 --reporter nyan tests/backend/specs +src/node_modules/mocha/bin/mocha --timeout 5000 --reporter nyan tests/backend/specs/api diff --git a/tests/backend/specs/api.js b/tests/backend/specs/api/pad.js similarity index 85% rename from tests/backend/specs/api.js rename to tests/backend/specs/api/pad.js index 279f3088..efb3f6de 100644 --- a/tests/backend/specs/api.js +++ b/tests/backend/specs/api/pad.js @@ -4,7 +4,7 @@ var assert = require('assert') api = supertest('http://localhost:9001'); path = require('path'); -var filePath = path.join(__dirname, '../../../APIKEY.txt'); +var filePath = path.join(__dirname, '../../../../APIKEY.txt'); var apiKey = fs.readFileSync(filePath, {encoding: 'utf-8'}); var apiVersion = 1; @@ -197,46 +197,6 @@ describe('getReadOnlyID', function(){ }); }) - - -/* Endpoints Still to interact with.. -padUsersCount(padID) -setPublicStatus(padID, publicStatus) -getPublicStatus(padID) -setPassword(padID, password) -isPasswordProtected(padID) -listAuthorsOfPad(padID) -getLastEdited(padID) -listSessionsOfGroup(groupID) -getSessionInfo(sessionID) -deleteSession(sessionID) -createSession(groupID, authorID, validUntil) -listPadsOfAuthor(authorID) -createAuthorIfNotExistsFor(authorMapper [, name]) -createAuthor([name]) -createGroupPad(groupID, padName [, text]) -listPads(groupID) -deleteGroup(groupID) -createGroupIfNotExistsFor(groupMapper) -createGroup() -*/ - - -/* -describe('getRevisionsCount', function(){ - it('gets the revision counts of a new pad', function(done) { - // This is broken because Etherpad doesn't handle HTTP codes properly see #2$ - // If your APIKey is password you deserve to fail all tests anyway - api.get(endPoint('getRevisionsCount')+"&padID="+testPadId) - .expect('Content-Type', /json/) - .expect(function(res){ - console.log(res.body); - }) - .expect(200, done) - }); -}) -*/ - var endPoint = function(point){ return '/api/'+apiVersion+'/'+point+'?apikey='+apiKey; } diff --git a/tests/backend/specs/api/sessionsAndGroups.js b/tests/backend/specs/api/sessionsAndGroups.js new file mode 100644 index 00000000..921724c7 --- /dev/null +++ b/tests/backend/specs/api/sessionsAndGroups.js @@ -0,0 +1,38 @@ +/* Endpoints Still to interact with.. +padUsersCount(padID) +setPublicStatus(padID, publicStatus) +getPublicStatus(padID) +setPassword(padID, password) +isPasswordProtected(padID) +listAuthorsOfPad(padID) +getLastEdited(padID) +listSessionsOfGroup(groupID) +getSessionInfo(sessionID) +deleteSession(sessionID) +createSession(groupID, authorID, validUntil) +listPadsOfAuthor(authorID) +createAuthorIfNotExistsFor(authorMapper [, name]) +createAuthor([name]) +createGroupPad(groupID, padName [, text]) +listPads(groupID) +deleteGroup(groupID) +createGroupIfNotExistsFor(groupMapper) +createGroup() +*/ + + +var endPoint = function(point){ + return '/api/'+apiVersion+'/'+point+'?apikey='+apiKey; +} + +function makeid() +{ + var text = ""; + var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + + for( var i=0; i < 5; i++ ){ + text += possible.charAt(Math.floor(Math.random() * possible.length)); + } + return text; +} + From f3c2ac6d94737de2f26c33fe9458fb1da39a25f4 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 26 Nov 2014 19:44:38 +0000 Subject: [PATCH 144/263] mowah pad tests, tea time --- tests/backend/specs/api/pad.js | 60 ++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/tests/backend/specs/api/pad.js b/tests/backend/specs/api/pad.js index efb3f6de..fa9863fb 100644 --- a/tests/backend/specs/api/pad.js +++ b/tests/backend/specs/api/pad.js @@ -9,6 +9,7 @@ var filePath = path.join(__dirname, '../../../../APIKEY.txt'); var apiKey = fs.readFileSync(filePath, {encoding: 'utf-8'}); var apiVersion = 1; var testPadId = makeid(); +var lastEdited = ""; describe('Connectivity', function(){ it('errors if can not connect', function(done) { @@ -43,7 +44,7 @@ describe('Permission', function(){ /* Pad Tests Order of execution -> deletePad -- This gives us a guaranteed clear environment -> createPad - -> getRevisions(0) -- Should be 0 + -> getRevisions -- Should be 0 -> getHTML -- Should be the default pad text in HTML format -> deletePad -- Should just delete a pad -> getHTML -- Should return an error @@ -54,6 +55,10 @@ describe('Permission', function(){ -> getRevisions -- Should be 0 still? -> padUsersCount -- Should be 0 -> getReadOnlyId -- Should be a value + -> listAuthorsOfPad(padID) -- should be empty array? + -> getLastEdited(padID) -- Should be when pad was made + -> setText(padId) + -> getLastEdited(padID) -- Should be when setText was performed */ describe('deletePad', function(){ @@ -176,7 +181,7 @@ describe('getRevisionsCount', function(){ }) describe('padUsersCount', function(){ - it('gets Revision Coutn of a Pad', function(done) { + it('gets User Count of a Pad', function(done) { api.get(endPoint('padUsersCount')+"&padID="+testPadId) .expect(function(res){ if(res.body.data.padUsersCount !== 0) throw new Error("Incorrect Pad User count") @@ -197,6 +202,57 @@ describe('getReadOnlyID', function(){ }); }) +describe('listAuthorsOfPad', function(){ + it('Get Authors of the Pad', function(done) { + api.get(endPoint('listAuthorsOfPad')+"&padID="+testPadId) + .expect(function(res){ + if(res.body.data.authorIDs.length !== 0) throw new Error("# of Authors of pad is not 0") + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('getLastEdited', function(){ + it('Get When Pad was left Edited', function(done) { + api.get(endPoint('getLastEdited')+"&padID="+testPadId) + .expect(function(res){ + if(!res.body.data.lastEdited){ + throw new Error("# of Authors of pad is not 0") + }else{ + lastEdited = res.body.data.lastEdited; + } + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('setText', function(){ + it('creates a new Pad with text', function(done) { + api.get(endPoint('setText')+"&padID="+testPadId+"&text=testTextTwo") + .expect(function(res){ + if(res.body.code !== 0) throw new Error("Pad setting text failed"); + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('getLastEdited', function(){ + it('Get When Pad was left Edited', function(done) { + api.get(endPoint('getLastEdited')+"&padID="+testPadId) + .expect(function(res){ + if(res.body.data.lastEdited <= lastEdited){ + throw new Error("Editing A Pad is not updating when it was last edited") + } + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + + var endPoint = function(point){ return '/api/'+apiVersion+'/'+point+'?apikey='+apiKey; } From 98cc725300de21c1cb80a26f9ec65fed829af144 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 26 Nov 2014 21:06:35 +0000 Subject: [PATCH 145/263] 30% of session and group tests done --- tests/backend/specs/api/sessionsAndGroups.js | 215 +++++++++++++++++-- 1 file changed, 194 insertions(+), 21 deletions(-) diff --git a/tests/backend/specs/api/sessionsAndGroups.js b/tests/backend/specs/api/sessionsAndGroups.js index 921724c7..41c16b4e 100644 --- a/tests/backend/specs/api/sessionsAndGroups.js +++ b/tests/backend/specs/api/sessionsAndGroups.js @@ -1,24 +1,198 @@ -/* Endpoints Still to interact with.. -padUsersCount(padID) -setPublicStatus(padID, publicStatus) -getPublicStatus(padID) -setPassword(padID, password) -isPasswordProtected(padID) -listAuthorsOfPad(padID) -getLastEdited(padID) -listSessionsOfGroup(groupID) -getSessionInfo(sessionID) -deleteSession(sessionID) -createSession(groupID, authorID, validUntil) -listPadsOfAuthor(authorID) -createAuthorIfNotExistsFor(authorMapper [, name]) -createAuthor([name]) -createGroupPad(groupID, padName [, text]) -listPads(groupID) -deleteGroup(groupID) -createGroupIfNotExistsFor(groupMapper) -createGroup() +var assert = require('assert') + supertest = require('supertest'), + fs = require('fs'), + api = supertest('http://localhost:9001'); + path = require('path'); + +var filePath = path.join(__dirname, '../../../../APIKEY.txt'); + +var apiKey = fs.readFileSync(filePath, {encoding: 'utf-8'}); +var apiVersion = 1; +var testPadId = makeid(); +var groupID = ""; +var authorID = ""; + +describe('API Versioning', function(){ + it('errors if can not connect', function(done) { + api.get('/api/') + .expect(function(res){ + apiVersion = res.body.currentVersion; + if (!res.body.currentVersion) throw new Error("No version set in API"); + return; + }) + .expect(200, done) + }); +}) + +/* Tests performed +-> createGroup() -- should return a groupID + -> listSessionsOfGroup(groupID) -- should be 0 + -> deleteGroup(groupID) + -> createGroupIfNotExistsFor(groupMapper) -- should return a groupID + + -> createAuthor([name]) -- should return an authorID + -> createAuthorIfNotExistsFor(authorMapper [, name]) -- should return an authorID + -> getAuthorName(authorID) -- should return a name IE "john" + -> listPadsOfAuthor(authorID) + +-> createSession(groupID, authorID, validUntil) + -> getSessionInfo(sessionID) + -> listSessionsOfGroup(groupID) -- should be 1 + -> deleteSession(sessionID) + -> getSessionInfo(sessionID) -- should have author id etc in + +-> listPads(groupID) -- should be empty array + -> createGroupPad(groupID, padName [, text]) + -> listPads(groupID) -- should be empty array + -> getPublicStatus(padId) + -> setPublicStatus(padId, status) + -> isPasswordProtected(padID) -- should be false + -> setPassword(padID, password) + -> isPasswordProtected(padID) -- should be true */ + +describe('createGroup', function(){ + it('creates a new group', function(done) { + api.get(endPoint('createGroup')) + .expect(function(res){ + if(res.body.code !== 0 || !res.body.data.groupID) throw new Error("Unable to create new Pad"); + groupID = res.body.data.groupID; + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('listSessionsOfGroup', function(){ + it('Lists the session of a group', function(done) { + api.get(endPoint('listSessionsOfGroup')+"&groupID="+groupID) + .expect(function(res){ + if(res.body.code !== 0 || res.body.data !== null) throw new Error("Sessions show as existing for this group"); + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('deleteGroup', function(){ + it('Deletes a group', function(done) { + api.get(endPoint('deleteGroup')+"&groupID="+groupID) + .expect(function(res){ + if(res.body.code !== 0) throw new Error("Group failed to be deleted"); + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('createGroupIfNotExistsFor', function(){ + it('Creates a group if one doesnt exist for mapper 0', function(done) { + api.get(endPoint('createGroupIfNotExistsFor')+"&groupMapper=management") + .expect(function(res){ + if(res.body.code !== 0 || !res.body.data.groupID) throw new Error("Sessions show as existing for this group"); + groupID = res.body.data.groupID + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('createAuthor', function(){ + it('Creates an author with a name set', function(done) { + api.get(endPoint('createAuthor')) + .expect(function(res){ + if(res.body.code !== 0 || !res.body.data.authorID) throw new Error("Sessions show as existing for this group"); + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('createAuthor', function(){ + it('Creates an author with a name set', function(done) { + api.get(endPoint('createAuthor')+"&name=john") + .expect(function(res){ + if(res.body.code !== 0 || !res.body.data.authorID) throw new Error("Unable to create user with name set"); + authorID = res.body.data.authorID; // we will be this author for the rest of the tests + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('createAuthorIfNotExistsFor', function(){ + it('Creates an author if it doesnt exist already and provides mapping', function(done) { + api.get(endPoint('createAuthorIfNotExistsFor')+"&authorMapper=chris") + .expect(function(res){ + if(res.body.code !== 0 || !res.body.data.authorID) throw new Error("Unable to create author with mapper"); + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('getAuthorName', function(){ + it('Gets the author name', function(done) { + api.get(endPoint('getAuthorName')+"&authorID="+authorID) + .expect(function(res){ + if(res.body.code !== 0 || !res.body.data === "john") throw new Error("Unable to get Author Name from Author ID"); + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + + +/* Endpoints Still to interact with.. + -> getAuthorName(authorID) -- should return a name IE "john" + -> listPadsOfAuthor(authorID) + +-> createSession(groupID, authorID, validUntil) + -> getSessionInfo(sessionID) + -> listSessionsOfGroup(groupID) -- should be 1 + -> deleteSession(sessionID) + -> getSessionInfo(sessionID) -- should have author id etc in + +-> listPads(groupID) -- should be empty array + -> createGroupPad(groupID, padName [, text]) + -> listPads(groupID) -- should be empty array + -> getPublicStatus(padId) + -> setPublicStatus(padId, status) + -> isPasswordProtected(padID) -- should be false + -> setPassword(padID, password) + -> isPasswordProtected(padID) -- should be true +*/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + var endPoint = function(point){ @@ -35,4 +209,3 @@ function makeid() } return text; } - From 253d6da2ac4b534d717caf2013aa265d9c1e1711 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 26 Nov 2014 21:19:55 +0000 Subject: [PATCH 146/263] 60% of backend api tests written --- tests/backend/specs/api/sessionsAndGroups.js | 75 +++++++++++++++++--- 1 file changed, 66 insertions(+), 9 deletions(-) diff --git a/tests/backend/specs/api/sessionsAndGroups.js b/tests/backend/specs/api/sessionsAndGroups.js index 41c16b4e..77e8b25d 100644 --- a/tests/backend/specs/api/sessionsAndGroups.js +++ b/tests/backend/specs/api/sessionsAndGroups.js @@ -11,6 +11,7 @@ var apiVersion = 1; var testPadId = makeid(); var groupID = ""; var authorID = ""; +var sessionID = ""; describe('API Versioning', function(){ it('errors if can not connect', function(done) { @@ -24,6 +25,10 @@ describe('API Versioning', function(){ }); }) +// BEGIN GROUP AND AUTHOR TESTS +///////////////////////////////////// +///////////////////////////////////// + /* Tests performed -> createGroup() -- should return a groupID -> listSessionsOfGroup(groupID) -- should be 0 @@ -142,17 +147,67 @@ describe('getAuthorName', function(){ }); }) +// BEGIN SESSION TESTS +/////////////////////////////////////// +/////////////////////////////////////// + +describe('createSession', function(){ + it('Creates a session for an Author', function(done) { + api.get(endPoint('createSession')+"&authorID="+authorID+"&groupID="+groupID+"&validUntil=999999999999") + .expect(function(res){ + if(res.body.code !== 0 || !res.body.data.sessionID) throw new Error("Unable to create Session"); + sessionID = res.body.data.sessionID; + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('getSessionInfo', function(){ + it('Gets session inf', function(done) { + api.get(endPoint('getSessionInfo')+"&sessionID="+sessionID) + .expect(function(res){ + if(res.body.code !== 0 || !res.body.data.groupID || !res.body.data.authorID || !res.body.data.validUntil) throw new Error("Unable to get Session info"); + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('listSessionsOfGroup', function(){ + it('Gets sessions of a group', function(done) { + api.get(endPoint('listSessionsOfGroup')+"&groupID="+groupID) + .expect(function(res){ + if(res.body.code !== 0 || typeof res.body.data !== "object") throw new Error("Unable to get sessions of a group"); + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('deleteSession', function(){ + it('Deletes a session', function(done) { + api.get(endPoint('deleteSession')+"&sessionID="+sessionID) + .expect(function(res){ + if(res.body.code !== 0) throw new Error("Unable to delete a session"); + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('getSessionInfo', function(){ + it('Gets session info', function(done) { + api.get(endPoint('getSessionInfo')+"&sessionID="+sessionID) + .expect(function(res){ + if(res.body.code !== 1) throw new Error("Session was not properly deleted"); + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) /* Endpoints Still to interact with.. - -> getAuthorName(authorID) -- should return a name IE "john" - -> listPadsOfAuthor(authorID) - --> createSession(groupID, authorID, validUntil) - -> getSessionInfo(sessionID) - -> listSessionsOfGroup(groupID) -- should be 1 - -> deleteSession(sessionID) - -> getSessionInfo(sessionID) -- should have author id etc in - -> listPads(groupID) -- should be empty array -> createGroupPad(groupID, padName [, text]) -> listPads(groupID) -- should be empty array @@ -161,6 +216,8 @@ describe('getAuthorName', function(){ -> isPasswordProtected(padID) -- should be false -> setPassword(padID, password) -> isPasswordProtected(padID) -- should be true + + -> listPadsOfAuthor(authorID) */ From 198e21167197becf8a7e907ebaaa6140914bfd33 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 26 Nov 2014 21:36:57 +0000 Subject: [PATCH 147/263] 4 more to go --- tests/backend/specs/api/sessionsAndGroups.js | 104 ++++++++++++++++--- 1 file changed, 90 insertions(+), 14 deletions(-) diff --git a/tests/backend/specs/api/sessionsAndGroups.js b/tests/backend/specs/api/sessionsAndGroups.js index 77e8b25d..583e2c36 100644 --- a/tests/backend/specs/api/sessionsAndGroups.js +++ b/tests/backend/specs/api/sessionsAndGroups.js @@ -12,6 +12,7 @@ var testPadId = makeid(); var groupID = ""; var authorID = ""; var sessionID = ""; +var padID = makeid(); describe('API Versioning', function(){ it('errors if can not connect', function(done) { @@ -51,9 +52,10 @@ describe('API Versioning', function(){ -> listPads(groupID) -- should be empty array -> getPublicStatus(padId) -> setPublicStatus(padId, status) - -> isPasswordProtected(padID) -- should be false - -> setPassword(padID, password) - -> isPasswordProtected(padID) -- should be true + -> getPublicStatus(padId) + -> isPasswordProtected(padID) -- should be false + -> setPassword(padID, password) + -> isPasswordProtected(padID) -- should be true */ describe('createGroup', function(){ @@ -95,7 +97,18 @@ describe('createGroupIfNotExistsFor', function(){ api.get(endPoint('createGroupIfNotExistsFor')+"&groupMapper=management") .expect(function(res){ if(res.body.code !== 0 || !res.body.data.groupID) throw new Error("Sessions show as existing for this group"); - groupID = res.body.data.groupID + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('createGroup', function(){ + it('creates a new group', function(done) { + api.get(endPoint('createGroup')) + .expect(function(res){ + if(res.body.code !== 0 || !res.body.data.groupID) throw new Error("Unable to create new Pad"); + groupID = res.body.data.groupID; }) .expect('Content-Type', /json/) .expect(200, done) @@ -207,17 +220,80 @@ describe('getSessionInfo', function(){ }); }) -/* Endpoints Still to interact with.. --> listPads(groupID) -- should be empty array - -> createGroupPad(groupID, padName [, text]) - -> listPads(groupID) -- should be empty array - -> getPublicStatus(padId) - -> setPublicStatus(padId, status) - -> isPasswordProtected(padID) -- should be false - -> setPassword(padID, password) - -> isPasswordProtected(padID) -- should be true +describe('listPads', function(){ + it('Lists Pads of a Group', function(done) { + api.get(endPoint('listPads')+"&groupID="+groupID) + .expect(function(res){ +console.log(res.body.data.padIDs); + if(res.body.code !== 0 || res.body.data.padIDs.length !== 0) throw new Error("Group already had pads for some reason"+res.body.data.padIDs); + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) - -> listPadsOfAuthor(authorID) +describe('createGroupPad', function(){ + it('Creates a Group Pad', function(done) { + api.get(endPoint('createGroupPad')+"&groupID="+groupID+"&padName="+padID) + .expect(function(res){ + if(res.body.code !== 0) throw new Error("Unable to create group pad"); + padID = res.body.data.padID; + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('listPads', function(){ + it('Lists Pads of a Group', function(done) { + api.get(endPoint('listPads')+"&groupID="+groupID) + .expect(function(res){ + if(res.body.code !== 0 || res.body.data.padIDs.length !== 1) throw new Error("Group isnt listing this pad"); + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('getPublicStatus', function(){ + it('Gets the public status of a pad', function(done) { + api.get(endPoint('getPublicStatus')+"&padID="+padID) + .expect(function(res){ + if(res.body.code !== 0 || res.body.data.publicstatus) throw new Error("Unable to get public status of this pad"); + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('setPublicStatus', function(){ + it('Sets the public status of a pad', function(done) { + api.get(endPoint('setPublicStatus')+"&padID="+padID+"&publicStatus=true") + .expect(function(res){ + if(res.body.code !== 0) throw new Error("Setting status did not work"); + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('getPublicStatus', function(){ + it('Gets the public status of a pad', function(done) { + api.get(endPoint('getPublicStatus')+"&padID="+padID) + .expect(function(res){ + if(res.body.code !== 0 || !res.body.data.publicStatus) throw new Error("Setting public status of this pad did not work"); + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + + +/* Endpoints Still to interact with.. +-> isPasswordProtected(padID) -- should be false + -> setPassword(padID, password) + -> isPasswordProtected(padID) -- should be true + -> listPadsOfAuthor(authorID) */ From e9115880b3792106fc072f0b63e549dc79f0d4bf Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 26 Nov 2014 21:44:50 +0000 Subject: [PATCH 148/263] final api test written for now --- tests/backend/specs/api/sessionsAndGroups.js | 91 ++++++++++++-------- 1 file changed, 55 insertions(+), 36 deletions(-) diff --git a/tests/backend/specs/api/sessionsAndGroups.js b/tests/backend/specs/api/sessionsAndGroups.js index 583e2c36..fc913748 100644 --- a/tests/backend/specs/api/sessionsAndGroups.js +++ b/tests/backend/specs/api/sessionsAndGroups.js @@ -39,7 +39,6 @@ describe('API Versioning', function(){ -> createAuthor([name]) -- should return an authorID -> createAuthorIfNotExistsFor(authorMapper [, name]) -- should return an authorID -> getAuthorName(authorID) -- should return a name IE "john" - -> listPadsOfAuthor(authorID) -> createSession(groupID, authorID, validUntil) -> getSessionInfo(sessionID) @@ -56,6 +55,8 @@ describe('API Versioning', function(){ -> isPasswordProtected(padID) -- should be false -> setPassword(padID, password) -> isPasswordProtected(padID) -- should be true + +-> listPadsOfAuthor(authorID) */ describe('createGroup', function(){ @@ -119,7 +120,7 @@ describe('createAuthor', function(){ it('Creates an author with a name set', function(done) { api.get(endPoint('createAuthor')) .expect(function(res){ - if(res.body.code !== 0 || !res.body.data.authorID) throw new Error("Sessions show as existing for this group"); + if(res.body.code !== 0 || !res.body.data.authorID) throw new Error("Unable to create author"); }) .expect('Content-Type', /json/) .expect(200, done) @@ -220,11 +221,14 @@ describe('getSessionInfo', function(){ }); }) +// GROUP PAD MANAGEMENT +/////////////////////////////////////// +/////////////////////////////////////// + describe('listPads', function(){ it('Lists Pads of a Group', function(done) { api.get(endPoint('listPads')+"&groupID="+groupID) .expect(function(res){ -console.log(res.body.data.padIDs); if(res.body.code !== 0 || res.body.data.padIDs.length !== 0) throw new Error("Group already had pads for some reason"+res.body.data.padIDs); }) .expect('Content-Type', /json/) @@ -255,6 +259,10 @@ describe('listPads', function(){ }); }) +// PAD SECURITY /-_-\ +/////////////////////////////////////// +/////////////////////////////////////// + describe('getPublicStatus', function(){ it('Gets the public status of a pad', function(done) { api.get(endPoint('getPublicStatus')+"&padID="+padID) @@ -288,43 +296,54 @@ describe('getPublicStatus', function(){ }); }) +describe('isPasswordProtected', function(){ + it('Gets the public status of a pad', function(done) { + api.get(endPoint('isPasswordProtected')+"&padID="+padID) + .expect(function(res){ + if(res.body.code !== 0 || res.body.data.isPasswordProtected) throw new Error("Pad is password protected by default"); + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) -/* Endpoints Still to interact with.. --> isPasswordProtected(padID) -- should be false - -> setPassword(padID, password) - -> isPasswordProtected(padID) -- should be true - -> listPadsOfAuthor(authorID) -*/ - - - - - - - - - - - - - - - - - - - - - - - - - - - +describe('setPassword', function(){ + it('Gets the public status of a pad', function(done) { + api.get(endPoint('setPassword')+"&padID="+padID+"&password=test") + .expect(function(res){ + if(res.body.code !== 0) throw new Error("Unabe to set password"); + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('isPasswordProtected', function(){ + it('Gets the public status of a pad', function(done) { + api.get(endPoint('isPasswordProtected')+"&padID="+padID) + .expect(function(res){ + if(res.body.code !== 0 || !res.body.data.isPasswordProtected) throw new Error("Pad password protection has not applied"); + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) +// NOT SURE HOW TO POPULAT THIS /-_-\ +/////////////////////////////////////// +/////////////////////////////////////// +describe('listPadsOfAuthor', function(){ + it('Gets the Pads of an Author', function(done) { + api.get(endPoint('listPadsOfAuthor')+"&authorID="+authorID) + .expect(function(res){ + if(res.body.code !== 0 || res.body.data.padIDs.length !== 0) throw new Error("Pad password protection has not applied"); + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) From cff8f4a61ebe48fe47cb2d9c54f41b9cff05781e Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 26 Nov 2014 21:58:27 +0000 Subject: [PATCH 149/263] remember to add supertest --- src/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/package.json b/src/package.json index 1408d9fe..f173b94e 100644 --- a/src/package.json +++ b/src/package.json @@ -41,7 +41,8 @@ "channels" : "0.0.x", "jsonminify" : "0.2.2", "measured" : "0.1.3", - "mocha" : ">=2.0.1" + "mocha" : ">=2.0.1", + "supertest" : ">=0.15.0" }, "bin": { "etherpad-lite": "./node/server.js" }, "devDependencies": { From fa5130978c3b47defbdb45b266e3c45c897d074d Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 26 Nov 2014 22:10:56 +0000 Subject: [PATCH 150/263] path issues for supertitties --- tests/backend/specs/api/pad.js | 2 +- tests/backend/specs/api/sessionsAndGroups.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/backend/specs/api/pad.js b/tests/backend/specs/api/pad.js index fa9863fb..80f77220 100644 --- a/tests/backend/specs/api/pad.js +++ b/tests/backend/specs/api/pad.js @@ -1,5 +1,5 @@ var assert = require('assert') - supertest = require('supertest'), + supertest = require(__dirname+'/../../../../src/node_modules/supertest'), fs = require('fs'), api = supertest('http://localhost:9001'); path = require('path'); diff --git a/tests/backend/specs/api/sessionsAndGroups.js b/tests/backend/specs/api/sessionsAndGroups.js index fc913748..86ba454a 100644 --- a/tests/backend/specs/api/sessionsAndGroups.js +++ b/tests/backend/specs/api/sessionsAndGroups.js @@ -1,5 +1,5 @@ var assert = require('assert') - supertest = require('supertest'), + supertest = require(__dirname+'/../../../../src/node_modules/supertest'), fs = require('fs'), api = supertest('http://localhost:9001'); path = require('path'); From e51ab2db249fea3f7ac1e64ffca576fb163703f3 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 26 Nov 2014 23:37:44 +0000 Subject: [PATCH 151/263] fix for #2132 --- src/static/js/ace2_inner.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index c9991a9a..ea47360b 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -3700,7 +3700,7 @@ function Ace2Inner(){ }, 0); specialHandled = true; } - if ((!specialHandled) && isTypeForCmdKey && String.fromCharCode(which).toLowerCase() == "s" && (evt.metaKey || evt.ctrlKey)) /* Do a saved revision on ctrl S */ + if ((!specialHandled) && isTypeForCmdKey && String.fromCharCode(which).toLowerCase() == "s" && (evt.metaKey || evt.ctrlKey) && !evt.altKey) /* Do a saved revision on ctrl S */ { evt.preventDefault(); var originalBackground = parent.parent.$('#revisionlink').css("background") From 2c728b8e1beb91269a007bfeb3c5f4821e219afa Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 27 Nov 2014 00:45:22 +0000 Subject: [PATCH 152/263] sanitizer change --- src/static/js/contentcollector.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index 048ed491..1268e279 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -32,7 +32,7 @@ var _ = require('./underscore'); function sanitizeUnicode(s) { - return UNorm.nfc(s).replace(/[\uffff\ufffe\ufeff\ufdd0-\ufdef\ud800-\udfff]/g, '?'); + return UNorm.nfc(s); } function makeContentCollector(collectStyles, browser, apool, domInterface, className2Author) From 61c6deda2e5ab82963f762abd5cc0a26e96ef413 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 27 Nov 2014 02:44:48 +0000 Subject: [PATCH 153/263] use a forked require-kernel --- src/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/package.json b/src/package.json index e69d9730..c6e07255 100644 --- a/src/package.json +++ b/src/package.json @@ -13,7 +13,7 @@ "dependencies" : { "yajsml" : "1.1.6", "request" : "2.9.100", - "require-kernel" : "1.0.5", + "etherpad-require-kernel" : "1.0.5", "resolve" : ">=1.0.0", "socket.io" : ">=1.2.0", "ueberDB" : ">=0.2.9", From c13301966f4d49d0cc70c295c3530c8c0fe6e7ef Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 27 Nov 2014 02:56:42 +0000 Subject: [PATCH 154/263] yeah need htis too --- src/node/utils/Minify.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node/utils/Minify.js b/src/node/utils/Minify.js index 58d08b30..e6b19542 100644 --- a/src/node/utils/Minify.js +++ b/src/node/utils/Minify.js @@ -28,7 +28,7 @@ var jsp = require("uglify-js").parser; var pro = require("uglify-js").uglify; var path = require('path'); var plugins = require("ep_etherpad-lite/static/js/pluginfw/plugins"); -var RequireKernel = require('require-kernel'); +var RequireKernel = require('etherpad-require-kernel'); var urlutil = require('url'); var ROOT_DIR = path.normalize(__dirname + "/../../static/"); From 3ca48ed97572b5ca697f8206df2d7bc3db61b4cc Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 27 Nov 2014 16:49:08 +0000 Subject: [PATCH 155/263] fix undo in chrome etc --- src/static/js/ace2_inner.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index ea47360b..fdcd134d 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -35,7 +35,7 @@ var isNodeText = Ace2Common.isNodeText, htmlPrettyEscape = Ace2Common.htmlPrettyEscape, noop = Ace2Common.noop; var hooks = require('./pluginfw/hooks'); - + var browser = require('./browser').browser; function Ace2Inner(){ @@ -51,7 +51,6 @@ function Ace2Inner(){ var SkipList = require('./skiplist'); var undoModule = require('./undomodule').undoModule; var AttributeManager = require('./AttributeManager'); - var browser = require('./browser'); var DEBUG = false; //$$ build script replaces the string "var DEBUG=true;//$$" with "var DEBUG=false;" // changed to false From ff58897679892f23d53f3372f40b40ccbacb7df5 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 27 Nov 2014 16:58:04 +0000 Subject: [PATCH 156/263] fix frontend tests --- tests/frontend/index.html | 3 +-- tests/frontend/lib/jquery.iframe.js | 5 +++-- tests/frontend/runner.js | 3 ++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/frontend/index.html b/tests/frontend/index.html index ac85a136..1bf10495 100644 --- a/tests/frontend/index.html +++ b/tests/frontend/index.html @@ -10,7 +10,7 @@
- + @@ -22,6 +22,5 @@ - diff --git a/tests/frontend/lib/jquery.iframe.js b/tests/frontend/lib/jquery.iframe.js index 3c3b7b05..604ae1bc 100644 --- a/tests/frontend/lib/jquery.iframe.js +++ b/tests/frontend/lib/jquery.iframe.js @@ -2,8 +2,9 @@ (function($) { $.fn.purgeFrame = function() { var deferred; + var browser = bowser; - if ($.browser.msie && parseFloat($.browser.version, 10) < 9) { + if (browser.msie && parseFloat(browser.version, 10) < 9) { deferred = purge(this); } else { this.remove(); @@ -36,4 +37,4 @@ return deferred.promise(); } -})(jQuery); \ No newline at end of file +})(jQuery); diff --git a/tests/frontend/runner.js b/tests/frontend/runner.js index 8f722125..4801b95c 100644 --- a/tests/frontend/runner.js +++ b/tests/frontend/runner.js @@ -162,7 +162,8 @@ $(function(){ } //allow cross iframe access - if ((!$.browser.msie) && (!($.browser.mozilla && $.browser.version.indexOf("1.8.") == 0))) { + var browser = bowser; + if ((!browser.msie) && (!(browser.mozilla && browser.version.indexOf("1.8.") == 0))) { document.domain = document.domain; // for comet } From 617514b335611b19730f03c9a352be1bbe701a59 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 27 Nov 2014 17:12:24 +0000 Subject: [PATCH 157/263] mst tests still brkoen but it runs --- tests/frontend/specs/bold.js | 2 +- tests/frontend/specs/caret.js | 9 +++++++-- tests/frontend/specs/indentation.js | 2 +- tests/frontend/specs/italic.js | 2 +- tests/frontend/specs/redo.js | 2 +- tests/frontend/specs/undo.js | 2 +- 6 files changed, 12 insertions(+), 7 deletions(-) diff --git a/tests/frontend/specs/bold.js b/tests/frontend/specs/bold.js index 95da7331..e023965f 100644 --- a/tests/frontend/specs/bold.js +++ b/tests/frontend/specs/bold.js @@ -43,7 +43,7 @@ describe("bold button", function(){ //select this text element $firstTextElement.sendkeys('{selectall}'); - if(inner$.browser.mozilla){ // if it's a mozilla browser + if(inner$(window)[0].bowser.mozilla){ // if it's a mozilla browser var evtType = "keypress"; }else{ var evtType = "keydown"; diff --git a/tests/frontend/specs/caret.js b/tests/frontend/specs/caret.js index b33f5168..a0cf0c3a 100644 --- a/tests/frontend/specs/caret.js +++ b/tests/frontend/specs/caret.js @@ -2,6 +2,12 @@ describe("As the caret is moved is the UI properly updated?", function(){ var padName; var numberOfRows = 50; + //create a new pad before each test run + beforeEach(function(cb){ + helper.newPad(cb); + this.timeout(60000); + }); + it("creates a pad", function(done) { padName = helper.newPad(done); this.timeout(60000); @@ -224,7 +230,6 @@ describe("As the caret is moved is the UI properly updated?", function(){ }); var i = 0; while(i < numberOfRows){ // press down arrow -console.log("dwn"); keyEvent(inner$, 40, false, false); i++; } @@ -287,7 +292,7 @@ function prepareDocument(n, target){ // generates a random document with random } function keyEvent(target, charCode, ctrl, shift){ // sends a charCode to the window - if(target.browser.mozilla){ // if it's a mozilla browser + if(inner$(window)[0].bowser.mozilla){ // if it's a mozilla browser var evtType = "keypress"; }else{ var evtType = "keydown"; diff --git a/tests/frontend/specs/indentation.js b/tests/frontend/specs/indentation.js index 3d14a7a8..74e32f97 100644 --- a/tests/frontend/specs/indentation.js +++ b/tests/frontend/specs/indentation.js @@ -15,7 +15,7 @@ describe("indentation button", function(){ //select this text element $firstTextElement.sendkeys('{selectall}'); - if(inner$.browser.mozilla){ // if it's a mozilla browser + if(inner$(window)[0].bowser.mozilla){ // if it's a mozilla browser var evtType = "keypress"; }else{ var evtType = "keydown"; diff --git a/tests/frontend/specs/italic.js b/tests/frontend/specs/italic.js index 29dbae59..979f922b 100644 --- a/tests/frontend/specs/italic.js +++ b/tests/frontend/specs/italic.js @@ -44,7 +44,7 @@ describe("italic some text", function(){ //select this text element $firstTextElement.sendkeys('{selectall}'); - if(inner$.browser.mozilla){ // if it's a mozilla browser + if(inner$(window)[0].bowser.mozilla){ // if it's a mozilla browser var evtType = "keypress"; }else{ var evtType = "keydown"; diff --git a/tests/frontend/specs/redo.js b/tests/frontend/specs/redo.js index c2f8a95a..bb955b26 100644 --- a/tests/frontend/specs/redo.js +++ b/tests/frontend/specs/redo.js @@ -47,7 +47,7 @@ describe("undo button then redo button", function(){ var modifiedValue = $firstTextElement.text(); // get the modified value expect(modifiedValue).not.to.be(originalValue); // expect the value to change - if(inner$.browser.mozilla){ // if it's a mozilla browser + if(inner$(window)[0].bowser.mozilla){ // if it's a mozilla browser var evtType = "keypress"; }else{ var evtType = "keydown"; diff --git a/tests/frontend/specs/undo.js b/tests/frontend/specs/undo.js index 0c58c9b8..31a47468 100644 --- a/tests/frontend/specs/undo.js +++ b/tests/frontend/specs/undo.js @@ -44,7 +44,7 @@ describe("undo button", function(){ var modifiedValue = $firstTextElement.text(); // get the modified value expect(modifiedValue).not.to.be(originalValue); // expect the value to change - if(inner$.browser.mozilla){ // if it's a mozilla browser + if(inner$(window)[0].bowser.mozilla){ // if it's a mozilla browser var evtType = "keypress"; }else{ var evtType = "keydown"; From 47029f2e6bdc02f4d7b19cbb52ea2e7399edcb53 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 27 Nov 2014 23:10:40 +0000 Subject: [PATCH 158/263] diable caret tests for now --- tests/frontend/specs/caret.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/frontend/specs/caret.js b/tests/frontend/specs/caret.js index a0cf0c3a..c1cdc724 100644 --- a/tests/frontend/specs/caret.js +++ b/tests/frontend/specs/caret.js @@ -1,6 +1,7 @@ describe("As the caret is moved is the UI properly updated?", function(){ var padName; var numberOfRows = 50; +/* //create a new pad before each test run beforeEach(function(cb){ @@ -8,10 +9,11 @@ describe("As the caret is moved is the UI properly updated?", function(){ this.timeout(60000); }); - it("creates a pad", function(done) { + xit("creates a pad", function(done) { padName = helper.newPad(done); this.timeout(60000); }); +*/ /* Tests to do * Keystroke up (38), down (40), left (37), right (39) with and without special keys IE control / shift @@ -26,10 +28,12 @@ describe("As the caret is moved is the UI properly updated?", function(){ * How do we keep the authors focus on a line if the lines above the author are modified? We should only redraw the user to a location if they are typing and make sure shift and arrow keys aren't redrawing the UI else highlight - copy/paste would get broken * How can we simulate an edit event in the test framework? */ - - // THIS DOESNT WORK AS IT DOESNT MOVE THE CURSOR! +/* + // THIS DOESNT WORK IN CHROME AS IT DOESNT MOVE THE CURSOR! it("down arrow", function(done){ var inner$ = helper.padInner$; + var chrome$ = helper.padChrome$; + var $newFirstTextElement = inner$("div").first(); $newFirstTextElement.focus(); keyEvent(inner$, 37, false, false); // arrow down @@ -37,9 +41,10 @@ describe("As the caret is moved is the UI properly updated?", function(){ done(); }); -/* + it("Creates N lines", function(done){ var inner$ = helper.padInner$; +console.log(inner$); var chrome$ = helper.padChrome$; var $newFirstTextElement = inner$("div").first(); From d36911da3003cb09845bd499b6768fe98cdf0581 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 27 Nov 2014 23:36:19 +0000 Subject: [PATCH 159/263] fix timeslider tests --- tests/frontend/specs/timeslider_revisions.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/frontend/specs/timeslider_revisions.js b/tests/frontend/specs/timeslider_revisions.js index 67938134..76fde33a 100644 --- a/tests/frontend/specs/timeslider_revisions.js +++ b/tests/frontend/specs/timeslider_revisions.js @@ -4,7 +4,8 @@ describe("timeslider", function(){ helper.newPad(cb); this.timeout(6000); }); - it("loads adds a hundred revisions", function(done) { + + it("loads adds a hundred revisions", function(done) { // passes var inner$ = helper.padInner$; var chrome$ = helper.padChrome$; @@ -56,7 +57,10 @@ describe("timeslider", function(){ }, 6000); }, revs*timePerRev); }); - it("changes the url when clicking on the timeslider", function(done) { + + + // Disabled as jquery trigger no longer works properly + xit("changes the url when clicking on the timeslider", function(done) { var inner$ = helper.padInner$; var chrome$ = helper.padChrome$; @@ -80,7 +84,6 @@ describe("timeslider", function(){ var $sliderBar = timeslider$('#ui-slider-bar'); var latestContents = timeslider$('#padcontent').text(); - var oldUrl = $('#iframe-container iframe')[0].contentWindow.location.hash; // Click somewhere on the timeslider @@ -111,6 +114,7 @@ describe("timeslider", function(){ var oldLength = inner$('body').text().length + newLines / 2; expect( oldLength ).to.not.eql( 0 ); inner$("div").first().sendkeys('a'); + var timeslider$; // wait for our additional revision to be added helper.waitFor(function(){ @@ -140,6 +144,7 @@ describe("timeslider", function(){ }); }); }); + it("checks the export url", function(done) { var inner$ = helper.padInner$; var chrome$ = helper.padChrome$; From 936e92e044796c9bdd5290a270de7a91fd7f1753 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 27 Nov 2014 23:48:14 +0000 Subject: [PATCH 160/263] fix tests in FF --- tests/frontend/specs/bold.js | 4 +++- tests/frontend/specs/caret.js | 2 +- tests/frontend/specs/indentation.js | 2 +- tests/frontend/specs/italic.js | 2 +- tests/frontend/specs/redo.js | 2 +- tests/frontend/specs/responsiveness.js | 2 +- tests/frontend/specs/undo.js | 2 +- 7 files changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/frontend/specs/bold.js b/tests/frontend/specs/bold.js index e023965f..703d0815 100644 --- a/tests/frontend/specs/bold.js +++ b/tests/frontend/specs/bold.js @@ -43,7 +43,9 @@ describe("bold button", function(){ //select this text element $firstTextElement.sendkeys('{selectall}'); - if(inner$(window)[0].bowser.mozilla){ // if it's a mozilla browser + console.log(inner$(window)[0].bowser); + + if(inner$(window)[0].bowser.firefox){ // if it's a mozilla browser var evtType = "keypress"; }else{ var evtType = "keydown"; diff --git a/tests/frontend/specs/caret.js b/tests/frontend/specs/caret.js index c1cdc724..48dd94a6 100644 --- a/tests/frontend/specs/caret.js +++ b/tests/frontend/specs/caret.js @@ -297,7 +297,7 @@ function prepareDocument(n, target){ // generates a random document with random } function keyEvent(target, charCode, ctrl, shift){ // sends a charCode to the window - if(inner$(window)[0].bowser.mozilla){ // if it's a mozilla browser + if(inner$(window)[0].bowser.firefox){ // if it's a mozilla browser var evtType = "keypress"; }else{ var evtType = "keydown"; diff --git a/tests/frontend/specs/indentation.js b/tests/frontend/specs/indentation.js index 74e32f97..3730f6bf 100644 --- a/tests/frontend/specs/indentation.js +++ b/tests/frontend/specs/indentation.js @@ -15,7 +15,7 @@ describe("indentation button", function(){ //select this text element $firstTextElement.sendkeys('{selectall}'); - if(inner$(window)[0].bowser.mozilla){ // if it's a mozilla browser + if(inner$(window)[0].bowser.firefox){ // if it's a mozilla browser var evtType = "keypress"; }else{ var evtType = "keydown"; diff --git a/tests/frontend/specs/italic.js b/tests/frontend/specs/italic.js index 979f922b..955cec0f 100644 --- a/tests/frontend/specs/italic.js +++ b/tests/frontend/specs/italic.js @@ -44,7 +44,7 @@ describe("italic some text", function(){ //select this text element $firstTextElement.sendkeys('{selectall}'); - if(inner$(window)[0].bowser.mozilla){ // if it's a mozilla browser + if(inner$(window)[0].bowser.firefox){ // if it's a mozilla browser var evtType = "keypress"; }else{ var evtType = "keydown"; diff --git a/tests/frontend/specs/redo.js b/tests/frontend/specs/redo.js index bb955b26..99056fb7 100644 --- a/tests/frontend/specs/redo.js +++ b/tests/frontend/specs/redo.js @@ -47,7 +47,7 @@ describe("undo button then redo button", function(){ var modifiedValue = $firstTextElement.text(); // get the modified value expect(modifiedValue).not.to.be(originalValue); // expect the value to change - if(inner$(window)[0].bowser.mozilla){ // if it's a mozilla browser + if(inner$(window)[0].bowser.firefox){ // if it's a mozilla browser var evtType = "keypress"; }else{ var evtType = "keydown"; diff --git a/tests/frontend/specs/responsiveness.js b/tests/frontend/specs/responsiveness.js index 44bdd611..ff7dace1 100644 --- a/tests/frontend/specs/responsiveness.js +++ b/tests/frontend/specs/responsiveness.js @@ -19,7 +19,7 @@ describe('Responsiveness of Editor', function() { helper.newPad(cb); this.timeout(6000); }); - it('Fast response to keypress in pad with large amount of contents', function(done) { + xit('Fast response to keypress in pad with large amount of contents', function(done) { var inner$ = helper.padInner$; var chrome$ = helper.padChrome$; var chars = '0000000000'; // row of placeholder chars diff --git a/tests/frontend/specs/undo.js b/tests/frontend/specs/undo.js index 31a47468..b8b7c785 100644 --- a/tests/frontend/specs/undo.js +++ b/tests/frontend/specs/undo.js @@ -44,7 +44,7 @@ describe("undo button", function(){ var modifiedValue = $firstTextElement.text(); // get the modified value expect(modifiedValue).not.to.be(originalValue); // expect the value to change - if(inner$(window)[0].bowser.mozilla){ // if it's a mozilla browser + if(inner$(window)[0].bowser.firefox){ // if it's a mozilla browser var evtType = "keypress"; }else{ var evtType = "keydown"; From 8b787b8d23fa65ad82566ee4f3bacc9c9d71b650 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 27 Nov 2014 23:58:50 +0000 Subject: [PATCH 161/263] readme docs for backend tests --- tests/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/README.md b/tests/README.md index 1851305f..201ee4c8 100644 --- a/tests/README.md +++ b/tests/README.md @@ -3,3 +3,7 @@ ## Frontend To run the tests, point your browser to `/tests/frontend` + +## Backend + +To run the tests, run ``bin/backendTests.sh`` From e6d85bbe69de34089039e28ae984807c0b37883f Mon Sep 17 00:00:00 2001 From: John McLear Date: Fri, 28 Nov 2014 00:26:34 +0000 Subject: [PATCH 162/263] fix issue with top of chatbox not being aligned properly --- src/static/js/pad_editbar.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/static/js/pad_editbar.js b/src/static/js/pad_editbar.js index bdf2d556..73a968ee 100644 --- a/src/static/js/pad_editbar.js +++ b/src/static/js/pad_editbar.js @@ -184,6 +184,9 @@ var padeditbar = (function() var containerTop = $('.menu_left').height() + 7 + "px"; $('#editbar').css("height", editbarHeight); $('#editorcontainer').css("top", containerTop); + if($('#options-stickychat').is(":checked")){ + $('#chatbox').css("top", containerTop); + }; }, registerDropdownCommand: function (cmd, dropdown) { dropdown = dropdown || cmd; From 68979e12123f010c3fbcd5c460af09b7ccdc13b5 Mon Sep 17 00:00:00 2001 From: John McLear Date: Fri, 28 Nov 2014 00:35:46 +0000 Subject: [PATCH 163/263] better fix --- src/static/js/pad_editbar.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/static/js/pad_editbar.js b/src/static/js/pad_editbar.js index 73a968ee..76a79a62 100644 --- a/src/static/js/pad_editbar.js +++ b/src/static/js/pad_editbar.js @@ -183,9 +183,10 @@ var padeditbar = (function() var editbarHeight = $('.menu_left').height() + 2 + "px"; var containerTop = $('.menu_left').height() + 7 + "px"; $('#editbar').css("height", editbarHeight); + $('#editorcontainer').css("top", containerTop); if($('#options-stickychat').is(":checked")){ - $('#chatbox').css("top", containerTop); + $('#chatbox').css("top", $('#editorcontainer').offset().top + "px"); }; }, registerDropdownCommand: function (cmd, dropdown) { From a642deaa72eef3e6e457271de93156ccac010696 Mon Sep 17 00:00:00 2001 From: John McLear Date: Fri, 28 Nov 2014 02:25:21 +0000 Subject: [PATCH 164/263] gritter css fix --- src/static/css/pad.css | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/static/css/pad.css b/src/static/css/pad.css index 5045f299..8b7e8258 100644 --- a/src/static/css/pad.css +++ b/src/static/css/pad.css @@ -1057,6 +1057,7 @@ input[type=checkbox] { right:20px; width:301px; z-index:9999; + background-color:#666; } #gritter-notice-wrapper.bottom-right { top: auto; @@ -1070,14 +1071,12 @@ input[type=checkbox] { } .gritter-top { - background:url(../../static/img/gritter.png) no-repeat left -30px; height:10px; } .hover .gritter-top { background-position:right -30px; } .gritter-bottom { - background:url(../../static/img/gritter.png) no-repeat left bottom; height:8px; margin:0; } @@ -1086,7 +1085,6 @@ input[type=checkbox] { } .gritter-item { display:block; - background:url(../../static/img/gritter.png) no-repeat left -40px; color:#eee; padding:2px 11px 8px 11px; font-size: 11px; @@ -1104,7 +1102,6 @@ input[type=checkbox] { position:absolute; top:5px; left:3px; - background:url('../../static/img/gritter.png') no-repeat left top; cursor:pointer; width:30px; height:30px; From c6d7ed114ea3bbb8fc700f316fcaa145d290c4af Mon Sep 17 00:00:00 2001 From: John McLear Date: Fri, 28 Nov 2014 16:27:12 +0000 Subject: [PATCH 165/263] script to update all plugins with one command on CLI --- bin/updatePlugins.sh | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100755 bin/updatePlugins.sh diff --git a/bin/updatePlugins.sh b/bin/updatePlugins.sh new file mode 100755 index 00000000..d696eca7 --- /dev/null +++ b/bin/updatePlugins.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +#Move to the folder where ep-lite is installed +cd `dirname $0` + +#Was this script started in the bin folder? if yes move out +if [ -d "../bin" ]; then + cd "../" +fi + +npm outdated --depth=0 | grep -v "^Package" | awk '{print $1}' | xargs npm install $1 --save-dev + From 3224122f7e27bd3ec9ce3f419ab4025c653b8010 Mon Sep 17 00:00:00 2001 From: John McLear Date: Fri, 28 Nov 2014 17:47:55 +0000 Subject: [PATCH 166/263] correct kernel v# --- src/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/package.json b/src/package.json index 7a8cd74e..98625622 100644 --- a/src/package.json +++ b/src/package.json @@ -12,13 +12,13 @@ ], "dependencies" : { "yajsml" : "1.1.6", - "request" : "2.9.100", - "etherpad-require-kernel" : "1.0.5", + "request" : ">=2.48.0", + "etherpad-require-kernel" : "1.0.6", "resolve" : ">=1.0.0", "socket.io" : ">=1.2.0", "ueberDB" : ">=0.2.9", "express" : ">3.1.0 <3.9.0", - "async" : "0.1.x", + "async" : "0.9.0", "connect" : "2.7.x", "clean-css" : "0.3.2", "uglify-js" : "1.2.5", From 74ffbdea7908f5fbbb0844815c62c67e0c842a74 Mon Sep 17 00:00:00 2001 From: John McLear Date: Fri, 28 Nov 2014 18:02:11 +0000 Subject: [PATCH 167/263] bump vs --- src/package.json | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/package.json b/src/package.json index 98625622..1e85ca53 100644 --- a/src/package.json +++ b/src/package.json @@ -13,34 +13,34 @@ "dependencies" : { "yajsml" : "1.1.6", "request" : ">=2.48.0", - "etherpad-require-kernel" : "1.0.6", + "etherpad-require-kernel" : ">=1.0.7", "resolve" : ">=1.0.0", "socket.io" : ">=1.2.0", "ueberDB" : ">=0.2.9", "express" : ">3.1.0 <3.9.0", - "async" : "0.9.0", + "async" : ">=0.9.0", "connect" : "2.7.x", - "clean-css" : "0.3.2", - "uglify-js" : "1.2.5", - "formidable" : "1.0.9", - "log4js" : "0.6.6", + "clean-css" : ">=0.50.x", + "uglify-js" : ">=2.4.15", + "formidable" : ">=1.0.15", + "log4js" : ">=0.6.21", "nodemailer" : "0.3.x", - "cheerio" : "0.18.0", - "async-stacktrace" : "0.0.2", - "npm" : "1.4.x", - "ejs" : "0.6.1", - "graceful-fs" : "1.1.5", - "slide" : "1.1.3", - "semver" : ">2.3.0", + "cheerio" : ">=0.18.0", + "async-stacktrace" : ">=0.0.2", + "npm" : ">=2.1.x", + "ejs" : ">=1.0.0", + "graceful-fs" : ">=3.0.4", + "slide" : ">=1.1.6", + "semver" : ">=2.3.0", "security" : "1.0.0", - "tinycon" : "0.0.1", + "tinycon" : ">=0.0.1", "underscore" : "1.5.1", - "unorm" : "1.0.0", - "languages4translatewiki" : "0.1.3", + "unorm" : ">=1.3.3", + "languages4translatewiki" : ">=0.1.3", "swagger-node-express" : ">=2.1.0", "channels" : "0.0.x", - "jsonminify" : "0.2.2", - "measured" : "0.1.3", + "jsonminify" : ">=0.2.3", + "measured" : ">=0.1.6", "mocha" : ">=2.0.1", "supertest" : ">=0.15.0" }, From 53887db872bb482e035ac331dfec70aad1c81a83 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 29 Nov 2014 00:26:25 +0000 Subject: [PATCH 168/263] mobile ui polish --- src/static/css/pad.css | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/static/css/pad.css b/src/static/css/pad.css index 8b7e8258..177ce91e 100644 --- a/src/static/css/pad.css +++ b/src/static/css/pad.css @@ -967,9 +967,10 @@ input[type=checkbox] { } @media only screen and (min-device-width: 320px) and (max-device-width: 720px) { #users { - top: 36px; - bottom: 40px; - border-radius: none; + top: auto; + right:0px !important; + bottom: 33px; + border-radius: 0px !important; } #mycolorpicker { left: -73px; @@ -989,6 +990,7 @@ input[type=checkbox] { background: -ms-linear-gradient(#f7f7f7, #f1f1f1 80%); background: linear-gradient(#f7f7f7, #f1f1f1 80%); width: 100%; + right:0px !important; overflow: hidden; height: 32px; position: fixed; @@ -1008,8 +1010,16 @@ input[type=checkbox] { .toolbar ul li a.selected { background: none !important } - #chaticon, #timesliderlink { - display: none !important + li[data-key="showusers"] > a { + + margin-top:-10px; + padding-top:2px !important; + line-height:20px; + vertical-align:top !important; + } + #chaticon { + position:absolute; + right:48px; } .popup { -webkit-border-radius: 0; @@ -1022,11 +1032,11 @@ input[type=checkbox] { width: 100%; } #settings, - #importexport, + #import_export, #connectivity, #embed { + top:auto; left: 0; - top: 0; bottom: 33px; right: 0; } @@ -1036,6 +1046,10 @@ input[type=checkbox] { #online_count { line-height: 24px } + #chatbox{ + position:absolute; + bottom:33px; + } } #passwordRequired{ From 34f76257396663b5cce19e75b0f91e394922e233 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 29 Nov 2014 00:30:13 +0000 Subject: [PATCH 169/263] a little more polish --- src/static/css/pad.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/static/css/pad.css b/src/static/css/pad.css index 177ce91e..0ec2a5f5 100644 --- a/src/static/css/pad.css +++ b/src/static/css/pad.css @@ -1050,6 +1050,10 @@ input[type=checkbox] { position:absolute; bottom:33px; } + #gritter-notice-wrapper{ + bottom:43px !important; + right:10px !important; + } } #passwordRequired{ From ee585f045755a36e468a10dc73d29755cfc9ec93 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 29 Nov 2014 00:32:37 +0000 Subject: [PATCH 170/263] color picker on mobile actually shows on screen --- src/static/css/pad.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/static/css/pad.css b/src/static/css/pad.css index 0ec2a5f5..57575eca 100644 --- a/src/static/css/pad.css +++ b/src/static/css/pad.css @@ -974,6 +974,8 @@ input[type=checkbox] { } #mycolorpicker { left: -73px; + top:auto !important; + bottom:33px !important; /* #mycolorpicker: width -#users: width */; } #editorcontainer { From ed96c13a1f4a7faa41c6437c9b65aa349a170c28 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 29 Nov 2014 00:54:06 +0000 Subject: [PATCH 171/263] minor ui polish --- src/static/js/pad_editbar.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/static/js/pad_editbar.js b/src/static/js/pad_editbar.js index 76a79a62..7e750a3e 100644 --- a/src/static/js/pad_editbar.js +++ b/src/static/js/pad_editbar.js @@ -180,8 +180,8 @@ var padeditbar = (function() return this; }, redrawHeight: function(){ - var editbarHeight = $('.menu_left').height() + 2 + "px"; - var containerTop = $('.menu_left').height() + 7 + "px"; + var editbarHeight = $('.menu_left').height() + 1 + "px"; + var containerTop = $('.menu_left').height() + 6 + "px"; $('#editbar').css("height", editbarHeight); $('#editorcontainer').css("top", containerTop); From 12f25d8a458bf007f8b442b1f3d6ef89b4f6e984 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 29 Nov 2014 01:31:04 +0000 Subject: [PATCH 172/263] bump cleancss --- src/node/utils/Minify.js | 5 +++-- src/package.json | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/node/utils/Minify.js b/src/node/utils/Minify.js index e6b19542..132d63da 100644 --- a/src/node/utils/Minify.js +++ b/src/node/utils/Minify.js @@ -23,7 +23,7 @@ var ERR = require("async-stacktrace"); var settings = require('./Settings'); var async = require('async'); var fs = require('fs'); -var cleanCSS = require('clean-css'); +var CleanCSS = require('clean-css'); var jsp = require("uglify-js").parser; var pro = require("uglify-js").uglify; var path = require('path'); @@ -411,7 +411,8 @@ function compressJS(values) function compressCSS(values) { var complete = values.join("\n"); - return cleanCSS.process(complete); + var cleanCSS = new CleanCSS({}).minify(complete); + return cleanCSS; } exports.minify = minify; diff --git a/src/package.json b/src/package.json index 1e85ca53..3d5f5e24 100644 --- a/src/package.json +++ b/src/package.json @@ -20,7 +20,7 @@ "express" : ">3.1.0 <3.9.0", "async" : ">=0.9.0", "connect" : "2.7.x", - "clean-css" : ">=0.50.x", + "clean-css" : ">=2.0.0", "uglify-js" : ">=2.4.15", "formidable" : ">=1.0.15", "log4js" : ">=0.6.21", From d7e980cd47552a73f74f6ef56fc429072bf54578 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 29 Nov 2014 02:24:19 +0000 Subject: [PATCH 173/263] yet more minor css polish --- src/static/css/pad.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/static/css/pad.css b/src/static/css/pad.css index 57575eca..2dd1663d 100644 --- a/src/static/css/pad.css +++ b/src/static/css/pad.css @@ -436,6 +436,12 @@ table#otheruserstable { height: 13px; overflow: hidden; margin: 0 4px; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } .usertdswatch { width: 1% From 6fea3a25f8c9fac94a4d97ccb60b0b1d5df96795 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 30 Nov 2014 21:21:58 +0000 Subject: [PATCH 174/263] stop death on no session --- src/node/handler/PadMessageHandler.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index 598b2df5..bc8f5d62 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -838,10 +838,10 @@ exports.updatePadClients = function(pad, callback) client.json.send(wireMsg); } - - sessioninfos[sid].time = currentTime; - sessioninfos[sid].rev = r; - + if(sessioninfos[sid]){ + sessioninfos[sid].time = currentTime; + sessioninfos[sid].rev = r; + } callback(null); } ], callback); From c539cc7d6b55b7aec30a8cf4c729b3b3e5acfad7 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 1 Dec 2014 15:10:03 +0000 Subject: [PATCH 175/263] styling of global view colum --- src/static/css/pad.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/static/css/pad.css b/src/static/css/pad.css index 8b7e8258..f59403fc 100644 --- a/src/static/css/pad.css +++ b/src/static/css/pad.css @@ -871,6 +871,7 @@ input[type=checkbox] { } .column { float: left; + width:50%; } #settings, #import_export, From 9c174023fc16184360ba186c9df1636cdf35f9c9 Mon Sep 17 00:00:00 2001 From: "Bjarni R. Einarsson" Date: Mon, 1 Dec 2014 22:39:42 +0000 Subject: [PATCH 176/263] Added dirty-db-cleaner.py --- bin/dirty-db-cleaner.py | 45 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100755 bin/dirty-db-cleaner.py diff --git a/bin/dirty-db-cleaner.py b/bin/dirty-db-cleaner.py new file mode 100755 index 00000000..8ed9c506 --- /dev/null +++ b/bin/dirty-db-cleaner.py @@ -0,0 +1,45 @@ +#!/usr/bin/python -u +# +# Created by Bjarni R. Einarsson, placed in the public domain. Go wild! +# +import json +import os +import sys + +try: + dirtydb_input = sys.argv[1] + dirtydb_output = '%s.new' % dirtydb_input + assert(os.path.exists(dirtydb_input)) + assert(not os.path.exists(dirtydb_output)) +except: + print + print 'Usage: %s /path/to/dirty.db' % sys.argv[0] + print + print 'Note: Will create a file named dirty.db.new in the same folder,' + print ' please make sure permissions are OK and a file by that' + print ' name does not exist already. This script works by omitting' + print ' duplicate lines from the dirty.db file, keeping only the' + print ' last (latest) instance. No revision data should be lost,' + print ' but be careful, make backups. If it breaks you get to keep' + print ' both pieces!' + print + sys.exit(1) + +dirtydb = {} +lines = 0 +with open(dirtydb_input, 'r') as fd: + print 'Reading %s' % dirtydb_input + for line in fd: + lines += 1 + data = json.loads(line) + dirtydb[data['key']] = line + if lines % 10000 == 0: + sys.stderr.write('.') +print +print 'OK, found %d unique keys in %d lines' % (len(dirtydb), lines) + +with open(dirtydb_output, 'w') as fd: + for data in dirtydb.values(): + fd.write(data) + +print 'Wrote data to %s. All done!' % dirtydb_output From d0896806d920aacba194ee9d67c30832fcea5b56 Mon Sep 17 00:00:00 2001 From: Sean Hussey Date: Wed, 3 Dec 2014 11:43:15 -0500 Subject: [PATCH 177/263] Fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a28e90fb..0cddb0b0 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ that allows your web application to manage pads, users and groups. It is recomme There is also a [jQuery plugin](https://github.com/ether/etherpad-lite-jquery-plugin) that helps you to embed Pads into your website. -There's also a full-featured plugin framework, allowing you to easily add your own features. By default your Etherpad is rather sparce and because Etherpad takes a lot of it's inspiration from Wordpress plugins are really easy to install and update. Once you have Etherpad installed you should visit the plugin page and take control. +There's also a full-featured plugin framework, allowing you to easily add your own features. By default your Etherpad is rather sparse and because Etherpad takes a lot of it's inspiration from Wordpress plugins are really easy to install and update. Once you have Etherpad installed you should visit the plugin page and take control. Finally, Etherpad comes with translations into most languages! Users are automatically delivered the correct language for their local settings. From c7b1aebfe82d4c9eda968f2a0b5a1337d12590be Mon Sep 17 00:00:00 2001 From: Timothy Chavez Date: Wed, 3 Dec 2014 20:11:39 -0600 Subject: [PATCH 178/263] Make changes based on code review Simplified the cloning process, added validation checks to ensure the new pad ID is valid and that a pad doesn't already exist with that ID. Also fixed a bug in the chatHead cloning loop and added the ability to specify a pad ID on the command the line (defaulting to the original "-rebuilt" pad ID formula) --- bin/rebuildPad.js | 77 +++++++++++++++++++++++------------------------ 1 file changed, 38 insertions(+), 39 deletions(-) diff --git a/bin/rebuildPad.js b/bin/rebuildPad.js index d349467b..c8383342 100644 --- a/bin/rebuildPad.js +++ b/bin/rebuildPad.js @@ -3,8 +3,8 @@ known "good" revision. */ -if(process.argv.length != 4) { - console.error("Use: node bin/repairPad.js $PADID $REV"); +if(process.argv.length != 4 && process.argv.length != 5) { + console.error("Use: node bin/repairPad.js $PADID $REV [$NEWPADID]"); process.exit(1); } @@ -14,10 +14,10 @@ var ueberDB = require("../src/node_modules/ueberDB"); var padId = process.argv[2]; var newRevHead = process.argv[3]; -var newPadId = padId + "-rebuilt"; +var newPadId = process.argv[4] || padId + "-rebuilt"; -var db, pad, newPad, settings; -var AuthorManager, ChangeSet, PadManager; +var db, oldPad, newPad, settings; +var AuthorManager, ChangeSet, Pad, PadManager; async.series([ function(callback) { @@ -35,26 +35,37 @@ async.series([ db = require('../src/node/db/DB'); db.init(callback); }, function(callback) { + PadManager = require('../src/node/db/PadManager'); + Pad = require('../src/node/db/Pad').Pad; // Get references to the original pad and to a newly created pad // HACK: This is a standalone script, so we want to write everything // out to the database immediately. The only problem with this is // that a driver (like the mysql driver) can hardcode these values. db.db.db.settings = {cache: 0, writeInterval: 0, json: true}; - PadManager = require('../src/node/db/PadManager'); - PadManager.getPad(padId, function(err, _pad) { - pad = _pad; - PadManager.getPad(newPadId, function(err, _newPad) { - newPad = _newPad; - callback(); - }); + // Validate the newPadId if specified and that a pad with that ID does + // not already exist to avoid overwriting it. + if (!PadManager.isValidPadId(newPadId)) { + console.error("Cannot create a pad with that id as it is invalid"); + process.exit(1); + } + PadManager.doesPadExists(newPadId, function(err, exists) { + if (exists) { + console.error("Cannot create a pad with that id as it already exists"); + process.exit(1); + } + }); + PadManager.getPad(padId, function(err, pad) { + oldPad = pad; + newPad = new Pad(newPadId); + callback(); }); }, function(callback) { // Clone all Chat revisions - var chatHead = pad.chatHead; - for(var i = 0; i <= chatHead; i++) { + var chatHead = oldPad.chatHead; + for(var i = 0, curHeadNum = 0; i <= chatHead; i++) { db.db.get("pad:" + padId + ":chat:" + i, function (err, chat) { - db.db.set("pad:" + newPadId + ":chat:" + i, chat); - console.log("Created: Chat Revision: pad:" + newPadId + ":chat:" + i) + db.db.set("pad:" + newPadId + ":chat:" + curHeadNum++, chat); + console.log("Created: Chat Revision: pad:" + newPadId + ":chat:" + curHeadNum); }); } callback(); @@ -65,28 +76,16 @@ async.series([ // Author attributes are derived from changesets, but there can also be // non-author attributes with specific mappings that changesets depend on // and, AFAICT, cannot be recreated any other way - newPad.pool.numToAttrib = pad.pool.numToAttrib; - for(var i = 1; i <= newRevHead; i++) { - db.db.get("pad:" + padId + ":revs:" + i, function(err, rev) { - var author = rev.meta.author; - var changeset = rev.changeset; - var newRev = ++newPad.head; - var newRevId = "pad:" + newPad.id + ":revs:" + newRev; - var newAtext = Changeset.applyToAText(changeset, newPad.atext, newPad.pool); - - AuthorManager.addPad(author, newPad.id); - newPad.pool.putAttrib(['author', author || '']); - - Changeset.copyAText(newAtext, newPad.atext); - + newPad.pool.numToAttrib = oldPad.pool.numToAttrib; + for(var curRevNum = 0; curRevNum <= newRevHead; curRevNum++) { + db.db.get("pad:" + padId + ":revs:" + curRevNum, function(err, rev) { + var newRevNum = ++newPad.head; + var newRevId = "pad:" + newPad.id + ":revs:" + newRevNum; db.db.set(newRevId, rev); - if(newRev % 100 == 0) { - db.db.setSub(newRevId, ["meta", "atext"], newPad.atext) - } - - console.log("Created: Revision: pad:" + newPad.id + ":revs:" + newRev); - - if (newRev == newRevHead) { + AuthorManager.addPad(rev.meta.author, newPad.id); + newPad.atext = Changeset.applyToAText(rev.changeset, newPad.atext, newPad.pool); + console.log("Created: Revision: pad:" + newPad.id + ":revs:" + newRevNum); + if (newRevNum == newRevHead) { callback(); } }); @@ -95,8 +94,8 @@ async.series([ // Add saved revisions up to the new revision head console.log(newPad.head); var newSavedRevisions = []; - for(var i in pad.savedRevisions) { - savedRev = pad.savedRevisions[i] + for(var i in oldPad.savedRevisions) { + savedRev = oldPad.savedRevisions[i] if (savedRev.revNum <= newRevHead) { newSavedRevisions.push(savedRev); console.log("Added: Saved Revision: " + savedRev.revNum); From 10c2f72720628c952138bc87dffd6505576fdcd0 Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Thu, 4 Dec 2014 16:01:39 +0100 Subject: [PATCH 179/263] dont remove more lines than exist in the whole text --- src/static/js/Changeset.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/static/js/Changeset.js b/src/static/js/Changeset.js index 355bef4a..366ad15f 100644 --- a/src/static/js/Changeset.js +++ b/src/static/js/Changeset.js @@ -903,6 +903,8 @@ exports.pack = function (oldLen, newLen, opsStr, bank) { * @params str {string} String to which a Changeset should be applied */ exports.applyToText = function (cs, str) { + var totalNrOfLines = str.split("\n").length; + var removedLines = 0; var unpacked = exports.unpack(cs); exports.assert(str.length == unpacked.oldLen, "mismatched apply: ", str.length, " / ", unpacked.oldLen); var csIter = exports.opIterator(unpacked.ops); @@ -916,6 +918,7 @@ exports.applyToText = function (cs, str) { assem.append(bankIter.take(op.chars)); break; case '-': + removedLines += op.lines; strIter.skip(op.chars); break; case '=': @@ -923,6 +926,7 @@ exports.applyToText = function (cs, str) { break; } } + exports.assert(totalNrOfLines >= removedLines,"cannot remove ", removedLines, " lines from text with ", totalNrOfLines, " lines"); assem.append(strIter.take(strIter.remaining())); return assem.toString(); }; From 5306f0c98af051b790bcf8344077e781b10f3eb7 Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Thu, 4 Dec 2014 16:05:02 +0100 Subject: [PATCH 180/263] wrap appendRevision in try-catch block --- src/node/handler/PadMessageHandler.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index bc8f5d62..1d89288c 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -742,7 +742,16 @@ function handleUserChanges(data, cb) return callback(new Error("Can't apply USER_CHANGES "+changeset+" with oldLen " + Changeset.oldLen(changeset) + " to document of length " + prevText.length)); } - pad.appendRevision(changeset, thisSession.author); + try + { + pad.appendRevision(changeset, thisSession.author); + } + catch(e) + { + client.json.send({disconnect:"badChangeset"}); + stats.meter('failedChangesets').mark(); + return callback(e) + } var correctionChangeset = _correctMarkersInPad(pad.atext, pad.pool); if (correctionChangeset) { From 22803da42f4f8c6aeccc15d0a916bae6332a8652 Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Thu, 4 Dec 2014 16:12:13 +0100 Subject: [PATCH 181/263] be more strict in checkRep --- src/static/js/Changeset.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/static/js/Changeset.js b/src/static/js/Changeset.js index 366ad15f..4d5487f8 100644 --- a/src/static/js/Changeset.js +++ b/src/static/js/Changeset.js @@ -255,20 +255,20 @@ exports.checkRep = function (cs) { var o = iter.next(); switch (o.opcode) { case '=': + exports.assert(o.chars >= o.lines, o.chars, " chars and ", o.lines, " lines in op ",cs); oldPos += o.chars; calcNewLen += o.chars; break; case '-': + exports.assert(o.chars >= o.lines, o.chars, " chars and ", o.lines, " lines in op ",cs); oldPos += o.chars; exports.assert(oldPos < oldLen, oldPos, " >= ", oldLen, " in ", cs); break; case '+': - { - calcNewLen += o.chars; - numInserted += o.chars; - exports.assert(calcNewLen < newLen, calcNewLen, " >= ", newLen, " in ", cs); - break; - } + exports.assert(o.chars >= o.lines, o.chars, " chars and ", o.lines, " lines in op ",cs); + calcNewLen += o.chars; + exports.assert(calcNewLen < newLen, calcNewLen, " >= ", newLen, " in ", cs); + break; } assem.append(o); } From 2218cbd252d23a188e772f03167d08dc05bd104c Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 8 Dec 2014 19:08:12 +0000 Subject: [PATCH 182/263] docs --- doc/api/hooks_server-side.md | 17 +++++++++++++++++ src/node/utils/ExportHtml.js | 5 +++++ 2 files changed, 22 insertions(+) diff --git a/doc/api/hooks_server-side.md b/doc/api/hooks_server-side.md index 435872ea..90f1b59c 100644 --- a/doc/api/hooks_server-side.md +++ b/doc/api/hooks_server-side.md @@ -247,6 +247,23 @@ Things in context: This hook will allow a plug-in developer to re-write each line when exporting to HTML. +## stylesForExport +Called from: src/node/utils/ExportHtml.js + +Things in context: + +1. padId - The Pad Id + +This hook will allow a plug-in developer to append Styles to the Exported HTML. + +Example: + +``` +exports.stylesForExport = function(hook, context){ + return("body{margin-left:20px;body{color:orange}"); +} +``` + ## exportFileName Called from src/node/handler/ExportHandler.js diff --git a/src/node/utils/ExportHtml.js b/src/node/utils/ExportHtml.js index 01920da7..16c67618 100644 --- a/src/node/utils/ExportHtml.js +++ b/src/node/utils/ExportHtml.js @@ -425,6 +425,9 @@ exports.getPadHTMLDocument = function (padId, revNum, noDocType, callback) { if(ERR(err, callback)) return; + // Include some Styles into the Head for Export + var stylesForExport = hooks.callAllStr("stylesForExport") || '' + var head = (noDocType ? '' : '\n') + '\n' + (noDocType ? '' : '\n' + @@ -442,6 +445,7 @@ exports.getPadHTMLDocument = function (padId, revNum, noDocType, callback) 'ol ol ol ol ol ol{ list-style-type: lower-roman; }' + 'ol ol ol ol ol ol ol { list-style-type: decimal; }' + 'ol ol ol ol ol ol ol ol{ list-style-type: lower-latin; }' + + stylesForExport + '\n' + '\n') + ''; @@ -452,6 +456,7 @@ exports.getPadHTMLDocument = function (padId, revNum, noDocType, callback) if(ERR(err, callback)) return; callback(null, head + html + foot); }); + }); }; From 7cf0e4a9ae1b6171c24cee72b5579e2774fdde45 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 8 Dec 2014 19:11:45 +0000 Subject: [PATCH 183/263] allow padId to be passed into the hook --- src/node/utils/ExportHtml.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node/utils/ExportHtml.js b/src/node/utils/ExportHtml.js index 16c67618..da2eb2b3 100644 --- a/src/node/utils/ExportHtml.js +++ b/src/node/utils/ExportHtml.js @@ -426,7 +426,7 @@ exports.getPadHTMLDocument = function (padId, revNum, noDocType, callback) if(ERR(err, callback)) return; // Include some Styles into the Head for Export - var stylesForExport = hooks.callAllStr("stylesForExport") || '' + var stylesForExport = hooks.callAllStr("stylesForExport", padId) || '' var head = (noDocType ? '' : '\n') + From f2c155ee1aa743be689ad898220462d13d2cc885 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 8 Dec 2014 19:44:40 +0000 Subject: [PATCH 184/263] blah use async --- src/node/utils/ExportHtml.js | 56 ++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/node/utils/ExportHtml.js b/src/node/utils/ExportHtml.js index da2eb2b3..bd6b160c 100644 --- a/src/node/utils/ExportHtml.js +++ b/src/node/utils/ExportHtml.js @@ -426,37 +426,37 @@ exports.getPadHTMLDocument = function (padId, revNum, noDocType, callback) if(ERR(err, callback)) return; // Include some Styles into the Head for Export - var stylesForExport = hooks.callAllStr("stylesForExport", padId) || '' + hooks.aCallAll("stylesForExport", padId, function(err, stylesForExport){ - var head = - (noDocType ? '' : '\n') + - '\n' + (noDocType ? '' : '\n' + - '' + Security.escapeHTML(padId) + '\n' + - '\n' + - '\n' + '\n') + - ''; + // Core inclusion of head etc. + var head = + (noDocType ? '' : '\n') + + '\n' + (noDocType ? '' : '\n' + + '' + Security.escapeHTML(padId) + '\n' + + '\n' + + '\n' + '\n') + + ''; + var foot = '\n\n'; - var foot = '\n\n'; - - getPadHTML(pad, revNum, function (err, html) - { - if(ERR(err, callback)) return; - callback(null, head + html + foot); + getPadHTML(pad, revNum, function (err, html) + { + if(ERR(err, callback)) return; + callback(null, head + html + foot); + }); }); - }); }; From ce004f9c59fbb7ea786ef4051498b2e895c5da37 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 8 Dec 2014 19:48:02 +0000 Subject: [PATCH 185/263] docs --- doc/api/hooks_server-side.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/api/hooks_server-side.md b/doc/api/hooks_server-side.md index 90f1b59c..e730f18e 100644 --- a/doc/api/hooks_server-side.md +++ b/doc/api/hooks_server-side.md @@ -259,8 +259,8 @@ This hook will allow a plug-in developer to append Styles to the Exported HTML. Example: ``` -exports.stylesForExport = function(hook, context){ - return("body{margin-left:20px;body{color:orange}"); +exports.stylesForExport = function(hook, padId, cb){ + cb("body{font-size:13.37em !important}"); } ``` From f11bdc1b440d5777b55e55e0313195b618f3d977 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 9 Dec 2014 01:31:40 +0000 Subject: [PATCH 186/263] also logic to add attributes for properties --- src/static/js/linestylefilter.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/static/js/linestylefilter.js b/src/static/js/linestylefilter.js index cb1ee1d5..757fac5a 100644 --- a/src/static/js/linestylefilter.js +++ b/src/static/js/linestylefilter.js @@ -34,7 +34,6 @@ var linestylefilter = {}; var _ = require('./underscore'); var AttributeManager = require('./AttributeManager'); - linestylefilter.ATTRIB_CLASSES = { 'bold': 'tag:b', 'italic': 'tag:i', @@ -59,6 +58,13 @@ linestylefilter.getAuthorClassName = function(author) linestylefilter.getLineStyleFilter = function(lineLength, aline, textAndClassFunc, apool) { + // Plugin Hook to add more Attrib Classes + hooks.aCallAll('aceAttribClasses', linestylefilter.ATTRIB_CLASSES, function(err, ATTRIB_CLASSES){ + if(ATTRIB_CLASSES){ + linestylefilter.ATTRIB_CLASSES = ATTRIB_CLASSES[0]; + } + }); + if (lineLength == 0) return textAndClassFunc; var nextAfterAuthorColors = textAndClassFunc; From b94a525e07a54529a4f1cfa3a9b328b551e13d70 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 9 Dec 2014 01:35:59 +0000 Subject: [PATCH 187/263] docs --- doc/api/hooks_client-side.md | 16 ++++++++++++++++ doc/api/hooks_server-side.md | 17 +++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/doc/api/hooks_client-side.md b/doc/api/hooks_client-side.md index b8a58b31..ca429a07 100644 --- a/doc/api/hooks_client-side.md +++ b/doc/api/hooks_client-side.md @@ -80,6 +80,22 @@ This hook is called during the attribute processing procedure, and should be use The return value for this function should be a list of classes, which will then be parsed into a valid class string. +## aceAttribClasses +Called from: src/static/js/linestylefilter.js + +Things in context: +1. Attributes - Object of Attributes + +This hook is called when attributes are investigated on a line. It is useful if you want to add another attribute type or property type to a pad. + +Example: +``` +exports.aceAttribClasses = function(hook_name, attr, cb){ + attr.sub = 'tag:sub'; + cb(attr); +} +``` + ## aceGetFilterStack Called from: src/static/js/linestylefilter.js diff --git a/doc/api/hooks_server-side.md b/doc/api/hooks_server-side.md index e730f18e..69f5ab5c 100644 --- a/doc/api/hooks_server-side.md +++ b/doc/api/hooks_server-side.md @@ -264,6 +264,23 @@ exports.stylesForExport = function(hook, padId, cb){ } ``` +## aceAttribClasses +Called from: src/static/js/linestylefilter.js + +Things in context: +1. Attributes - Object of Attributes + +This hook is called when attributes are investigated on a line. It is useful if you want to add another attribute type or property type to a pad. + +Example: + +``` +exports.aceAttribClasses = function(hook_name, attr, cb){ + attr.sub = 'tag:sub'; + cb(attr); +} +``` + ## exportFileName Called from src/node/handler/ExportHandler.js From 653cc6c87fe8956b17261cb6cb3b5fbc00128ba1 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 9 Dec 2014 14:57:18 +0000 Subject: [PATCH 188/263] this class was in before, it is used to show when a style is applied to a piece of content --- src/static/css/pad.css | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/static/css/pad.css b/src/static/css/pad.css index fb2cb82b..7ecf8e22 100644 --- a/src/static/css/pad.css +++ b/src/static/css/pad.css @@ -105,6 +105,17 @@ a img { -moz-box-shadow: 0 0 8px rgba(0,0,0,.1) inset; box-shadow: 0 0 8px rgba(0,0,0,.1) inset; } +.toolbar ul li .activeButton { + background: #eee; + background: -webkit-linear-gradient(#ddd, #fff); + background: -moz-linear-gradient(#ddd, #fff); + background: -o-linear-gradient(#ddd, #fff); + background: -ms-linear-gradient(#ddd, #fff); + background: linear-gradient(#ddd, #fff); + -webkit-box-shadow: 0 0 8px rgba(0,0,0,.1) inset; + -moz-box-shadow: 0 0 8px rgba(0,0,0,.1) inset; + box-shadow: 0 0 8px rgba(0,0,0,.1) inset; +} .toolbar ul li a { background: #fff; background: -webkit-linear-gradient(#fff, #f0f0f0); From 45c1ad078bda7800cec1a81f2b8517adec93c43d Mon Sep 17 00:00:00 2001 From: Simon Gaeremynck Date: Tue, 25 Nov 2014 18:42:40 +0000 Subject: [PATCH 189/263] Allow for shardable Etherpad --- src/static/js/pad.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/static/js/pad.js b/src/static/js/pad.js index 78c20657..89ebfa76 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.js @@ -170,7 +170,9 @@ function handshake() var resource = exports.baseURL.substring(1) + "socket.io"; //connect socket = pad.socket = io.connect(url, { - resource: resource, + // Allow deployers to host Etherpad on a non-root path + 'path': exports.baseURL + "socket.io", + 'resource': resource, 'max reconnection attempts': 3, 'sync disconnect on unload' : false }); From 3a2669b70488d26d2fbd9e0b20b8801fb8d7d8a5 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 9 Dec 2014 16:13:52 +0000 Subject: [PATCH 190/263] additional tag support hook --- src/node/utils/ExportHtml.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/node/utils/ExportHtml.js b/src/node/utils/ExportHtml.js index bd6b160c..cc32ea32 100644 --- a/src/node/utils/ExportHtml.js +++ b/src/node/utils/ExportHtml.js @@ -78,6 +78,14 @@ function getHTMLFromAtext(pad, atext, authorColors) var tags = ['h1', 'h2', 'strong', 'em', 'u', 's']; var props = ['heading1', 'heading2', 'bold', 'italic', 'underline', 'strikethrough']; + + hooks.aCallAll("exportHtmlAdditionalTags", pad, function(err, newProps){ + newProps.forEach(function (propName, i){ + tags.push(propName); + props.push(propName); + }); + }); + // holds a map of used styling attributes (*1, *2, etc) in the apool // and maps them to an index in props // *3:2 -> the attribute *3 means strong From fccfc3bd417c574d52ec14c30922f32d5e883004 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 9 Dec 2014 16:16:19 +0000 Subject: [PATCH 191/263] docs --- doc/api/hooks_server-side.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/doc/api/hooks_server-side.md b/doc/api/hooks_server-side.md index 69f5ab5c..251cbf11 100644 --- a/doc/api/hooks_server-side.md +++ b/doc/api/hooks_server-side.md @@ -298,6 +298,24 @@ exports.exportFileName = function(hook, padId, callback){ } ``` +## exportHtmlAdditionalTags +Called from src/node/utils/ExportHtml.js + +Things in context: + +1. Pad object + +This hook will allow a plug-in developer to include more properties and attributes to support during HTML Export. An Array should be returned. + +Example: +``` +// Add the props to be supported in export +exports.exportHtmlAdditionalTags = function(hook, pad, cb){ + var padId = pad.id; + cb(["massive","jugs"]); +}; + + ## userLeave Called from src/node/handler/PadMessageHandler.js From 197a41627d931e3ca6a6a756caefae05d09b7352 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 9 Dec 2014 17:34:52 +0000 Subject: [PATCH 192/263] allow plugins to add multiple styles --- src/node/utils/ExportHtml.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/node/utils/ExportHtml.js b/src/node/utils/ExportHtml.js index cc32ea32..693cf9cf 100644 --- a/src/node/utils/ExportHtml.js +++ b/src/node/utils/ExportHtml.js @@ -433,9 +433,12 @@ exports.getPadHTMLDocument = function (padId, revNum, noDocType, callback) { if(ERR(err, callback)) return; + var stylesForExportCSS = ""; // Include some Styles into the Head for Export hooks.aCallAll("stylesForExport", padId, function(err, stylesForExport){ - + stylesForExport.forEach(function(css){ + stylesForExportCSS += css; + }); // Core inclusion of head etc. var head = (noDocType ? '' : '\n') + @@ -454,7 +457,7 @@ exports.getPadHTMLDocument = function (padId, revNum, noDocType, callback) 'ol ol ol ol ol ol{ list-style-type: lower-roman; }' + 'ol ol ol ol ol ol ol { list-style-type: decimal; }' + 'ol ol ol ol ol ol ol ol{ list-style-type: lower-latin; }' + - stylesForExport + + stylesForExportCSS + '\n' + '\n') + ''; var foot = '\n\n'; From a2dbf8761f4cdffbfae3549ca85ea153b163063a Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 10 Dec 2014 01:23:24 +0000 Subject: [PATCH 193/263] allow for timeslider custom messages --- src/static/js/broadcast.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/static/js/broadcast.js b/src/static/js/broadcast.js index 191d802e..a25d889b 100644 --- a/src/static/js/broadcast.js +++ b/src/static/js/broadcast.js @@ -27,6 +27,7 @@ var Changeset = require('./Changeset'); var linestylefilter = require('./linestylefilter').linestylefilter; var colorutils = require('./colorutils').colorutils; var _ = require('./underscore'); +var hooks = require('./pluginfw/hooks'); // These parameters were global, now they are injected. A reference to the // Timeslider controller would probably be more appropriate. @@ -534,6 +535,7 @@ function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro var savedRev = obj.savedRev; BroadcastSlider.addSavedRevision(savedRev.revNum, savedRev); } + hooks.callAll('handleClientTimesliderMessage_' + obj.type, {payload: obj}); } else if(obj.type == "CHANGESET_REQ") { From 5d15f655f0d697995e221c19e707fc9ff05faaa2 Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Sun, 14 Dec 2014 22:01:28 +0100 Subject: [PATCH 194/263] dont make local variables global --- src/node/db/API.js | 6 +++--- src/node/db/Pad.js | 2 +- src/node/eejs/index.js | 2 +- src/node/hooks/express.js | 2 +- src/static/js/ace2_inner.js | 2 +- src/static/js/broadcast_revisions.js | 2 +- src/static/js/changesettracker.js | 2 +- src/static/js/collab_client.js | 4 ++-- src/static/js/contentcollector.js | 2 +- src/static/js/pluginfw/hooks.js | 2 +- src/static/js/timeslider.js | 2 +- 11 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/node/db/API.js b/src/node/db/API.js index 4a912368..2f6542e4 100644 --- a/src/node/db/API.js +++ b/src/node/db/API.js @@ -263,7 +263,7 @@ exports.getText = function(padID, rev, callback) { if(ERR(err, callback)) return; - data = {text: atext.text}; + var data = {text: atext.text}; callback(null, data); }) @@ -368,7 +368,7 @@ exports.getHTML = function(padID, rev, callback) if(ERR(err, callback)) return; html = "" +html; // adds HTML head html += ""; - data = {html: html}; + var data = {html: html}; callback(null, data); }); } @@ -380,7 +380,7 @@ exports.getHTML = function(padID, rev, callback) if(ERR(err, callback)) return; html = "" +html; // adds HTML head html += ""; - data = {html: html}; + var data = {html: html}; callback(null, data); }); } diff --git a/src/node/db/Pad.js b/src/node/db/Pad.js index 4670696a..2791334b 100644 --- a/src/node/db/Pad.js +++ b/src/node/db/Pad.js @@ -135,7 +135,7 @@ Pad.prototype.getRevisionDate = function getRevisionDate(revNum, callback) { Pad.prototype.getAllAuthors = function getAllAuthors() { var authors = []; - for(key in this.pool.numToAttrib) + for(var key in this.pool.numToAttrib) { if(this.pool.numToAttrib[key][0] == "author" && this.pool.numToAttrib[key][1] != "") { diff --git a/src/node/eejs/index.js b/src/node/eejs/index.js index 48185d80..30f5a442 100644 --- a/src/node/eejs/index.js +++ b/src/node/eejs/index.js @@ -71,7 +71,7 @@ exports.begin_define_block = function (name) { } exports.end_define_block = function () { - content = exports.end_capture(); + var content = exports.end_capture(); return content; } diff --git a/src/node/hooks/express.js b/src/node/hooks/express.js index c6573c80..e858b800 100644 --- a/src/node/hooks/express.js +++ b/src/node/hooks/express.js @@ -56,7 +56,7 @@ exports.restartServer = function () { console.log( "SSL -- server key file: " + settings.ssl.key ); console.log( "SSL -- Certificate Authority's certificate file: " + settings.ssl.cert ); - options = { + var options = { key: fs.readFileSync( settings.ssl.key ), cert: fs.readFileSync( settings.ssl.cert ) }; diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 07a9b971..d6c5c52c 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -5038,7 +5038,7 @@ function Ace2Inner(){ //3-renumber every list item of the same level from the beginning, level 1 //IMPORTANT: never skip a level because there imbrication may be arbitrary var builder = Changeset.builder(rep.lines.totalWidth()); - loc = [0,0]; + var loc = [0,0]; function applyNumberList(line, level) { //init diff --git a/src/static/js/broadcast_revisions.js b/src/static/js/broadcast_revisions.js index 1980bdf3..9774dc3f 100644 --- a/src/static/js/broadcast_revisions.js +++ b/src/static/js/broadcast_revisions.js @@ -48,7 +48,7 @@ function loadBroadcastRevisionsJS() }); } - revisionInfo = {}; + var revisionInfo = {}; revisionInfo.addChangeset = function(fromIndex, toIndex, changeset, backChangeset, timeDelta) { var startRevision = revisionInfo[fromIndex] || revisionInfo.createNew(fromIndex); diff --git a/src/static/js/changesettracker.js b/src/static/js/changesettracker.js index f3efc407..fe362c4b 100644 --- a/src/static/js/changesettracker.js +++ b/src/static/js/changesettracker.js @@ -179,7 +179,7 @@ function makeChangesetTracker(scheduler, apool, aceCallbacksProvider) // We need to replace all author attribs with thisSession.author, in case they copy/pasted or otherwise inserted other peoples changes if(apool.numToAttrib){ for (var attr in apool.numToAttrib){ - if (apool.numToAttrib[attr][0] == 'author' && apool.numToAttrib[attr][1] == authorId) authorAttr = Number(attr).toString(36) + if (apool.numToAttrib[attr][0] == 'author' && apool.numToAttrib[attr][1] == authorId) var authorAttr = Number(attr).toString(36) } // Replace all added 'author' attribs with the value of the current user diff --git a/src/static/js/collab_client.js b/src/static/js/collab_client.js index 146ec51b..f83e6d6c 100644 --- a/src/static/js/collab_client.js +++ b/src/static/js/collab_client.js @@ -152,7 +152,7 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options, _pad) // apply msgQueue changeset. if (msgQueue.length != 0) { - while (msg = msgQueue.shift()) { + while (var msg = msgQueue.shift()) { var newRev = msg.newRev; rev=newRev; if (msg.type == "ACCEPT_COMMIT") @@ -274,7 +274,7 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options, _pad) // When inInternationalComposition, msg pushed msgQueue. if (msgQueue.length > 0 || editor.getInInternationalComposition()) { - if (msgQueue.length > 0) oldRev = msgQueue[msgQueue.length - 1].newRev; + if (msgQueue.length > 0) var oldRev = msgQueue[msgQueue.length - 1].newRev; else oldRev = rev; if (newRev != (oldRev + 1)) diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index ee0a1c27..0a97498a 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -663,7 +663,7 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class { //var semiloc = oldString.lastIndexOf(';', lineLimit-1); //var lengthToTake = (semiloc >= 0 ? (semiloc+1) : lineLimit); - lengthToTake = lineLimit; + var lengthToTake = lineLimit; newStrings.push(oldString.substring(0, lengthToTake)); oldString = oldString.substring(lengthToTake); newAttribStrings.push(Changeset.subattribution(oldAttribString, 0, lengthToTake)); diff --git a/src/static/js/pluginfw/hooks.js b/src/static/js/pluginfw/hooks.js index a8ac413f..cf5fcc4e 100644 --- a/src/static/js/pluginfw/hooks.js +++ b/src/static/js/pluginfw/hooks.js @@ -41,7 +41,7 @@ exports.syncMapFirst = function (lst, fn) { exports.mapFirst = function (lst, fn, cb) { var i = 0; - next = function () { + var next = function () { if (i >= lst.length) return cb(undefined); fn(lst[i++], function (err, result) { if (err) return cb(err); diff --git a/src/static/js/timeslider.js b/src/static/js/timeslider.js index b3c10b8a..d00c5912 100644 --- a/src/static/js/timeslider.js +++ b/src/static/js/timeslider.js @@ -62,7 +62,7 @@ function init() { var resource = exports.baseURL.substring(1) + 'socket.io'; //build up the socket io connection - socket = io.connect(url, {resource: resource}); + var socket = io.connect(url, {resource: resource}); //send the ready message once we're connected socket.on('connect', function() From 0263d30b3771badc3db8b2d955e45422516e290d Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Tue, 16 Dec 2014 17:00:35 +0100 Subject: [PATCH 195/263] declare var outside of loop --- src/static/js/collab_client.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/static/js/collab_client.js b/src/static/js/collab_client.js index f83e6d6c..006b75f4 100644 --- a/src/static/js/collab_client.js +++ b/src/static/js/collab_client.js @@ -152,7 +152,8 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options, _pad) // apply msgQueue changeset. if (msgQueue.length != 0) { - while (var msg = msgQueue.shift()) { + var msg; + while (msg = msgQueue.shift()) { var newRev = msg.newRev; rev=newRev; if (msg.type == "ACCEPT_COMMIT") From b204aa20857e1d192f6e9d9f6aae130d3e59bd7c Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Tue, 16 Dec 2014 19:10:01 +0100 Subject: [PATCH 196/263] remove more dead requires. --- src/node/db/AuthorManager.js | 1 - src/node/db/SecurityManager.js | 1 - src/node/db/SessionStore.js | 2 -- src/node/handler/ExportHandler.js | 1 - src/node/hooks/express/adminplugins.js | 2 -- src/node/hooks/express/adminsettings.js | 2 -- src/node/hooks/express/padreadonly.js | 1 - src/node/hooks/express/socketio.js | 1 - src/node/hooks/express/static.js | 3 --- src/node/hooks/express/swagger.js | 1 - src/node/hooks/express/webaccess.js | 1 - src/node/hooks/i18n.js | 1 - src/node/utils/Abiword.js | 1 - src/node/utils/ExportHelper.js | 5 ----- src/node/utils/ExportTxt.js | 6 ------ src/node/utils/Minify.js | 1 - src/node/utils/caching_middleware.js | 1 - src/node/utils/toolbar.js | 4 +--- 18 files changed, 1 insertion(+), 34 deletions(-) diff --git a/src/node/db/AuthorManager.js b/src/node/db/AuthorManager.js index 5ba608e9..e0f569ef 100644 --- a/src/node/db/AuthorManager.js +++ b/src/node/db/AuthorManager.js @@ -21,7 +21,6 @@ var ERR = require("async-stacktrace"); var db = require("./DB").db; -var async = require("async"); var customError = require("../utils/customError"); var randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString; diff --git a/src/node/db/SecurityManager.js b/src/node/db/SecurityManager.js index df3c3826..6fae57ff 100644 --- a/src/node/db/SecurityManager.js +++ b/src/node/db/SecurityManager.js @@ -20,7 +20,6 @@ var ERR = require("async-stacktrace"); -var db = require("./DB").db; var async = require("async"); var authorManager = require("./AuthorManager"); var padManager = require("./PadManager"); diff --git a/src/node/db/SessionStore.js b/src/node/db/SessionStore.js index 52a504f1..5c45ddb3 100644 --- a/src/node/db/SessionStore.js +++ b/src/node/db/SessionStore.js @@ -5,8 +5,6 @@ */ var Store = require('ep_etherpad-lite/node_modules/connect/lib/middleware/session/store'), - utils = require('ep_etherpad-lite/node_modules/connect/lib/utils'), - Session = require('ep_etherpad-lite/node_modules/connect/lib/middleware/session/session'), db = require('ep_etherpad-lite/node/db/DB').db, log4js = require('ep_etherpad-lite/node_modules/log4js'), messageLogger = log4js.getLogger("SessionStore"); diff --git a/src/node/handler/ExportHandler.js b/src/node/handler/ExportHandler.js index 5bedcce2..017f76b5 100644 --- a/src/node/handler/ExportHandler.js +++ b/src/node/handler/ExportHandler.js @@ -22,7 +22,6 @@ var ERR = require("async-stacktrace"); var exporthtml = require("../utils/ExportHtml"); var exporttxt = require("../utils/ExportTxt"); var exportdokuwiki = require("../utils/ExportDokuWiki"); -var padManager = require("../db/PadManager"); var async = require("async"); var fs = require("fs"); var settings = require('../utils/Settings'); diff --git a/src/node/hooks/express/adminplugins.js b/src/node/hooks/express/adminplugins.js index ded1f660..8e372f1c 100644 --- a/src/node/hooks/express/adminplugins.js +++ b/src/node/hooks/express/adminplugins.js @@ -1,10 +1,8 @@ -var path = require('path'); var eejs = require('ep_etherpad-lite/node/eejs'); var installer = require('ep_etherpad-lite/static/js/pluginfw/installer'); var plugins = require('ep_etherpad-lite/static/js/pluginfw/plugins'); var _ = require('underscore'); var semver = require('semver'); -var async = require('async'); exports.expressCreateServer = function (hook_name, args, cb) { args.app.get('/admin/plugins', function(req, res) { diff --git a/src/node/hooks/express/adminsettings.js b/src/node/hooks/express/adminsettings.js index 42a07c73..4986f093 100644 --- a/src/node/hooks/express/adminsettings.js +++ b/src/node/hooks/express/adminsettings.js @@ -1,7 +1,5 @@ -var path = require('path'); var eejs = require('ep_etherpad-lite/node/eejs'); var settings = require('ep_etherpad-lite/node/utils/Settings'); -var installer = require('ep_etherpad-lite/static/js/pluginfw/installer'); var hooks = require("ep_etherpad-lite/static/js/pluginfw/hooks"); var fs = require('fs'); diff --git a/src/node/hooks/express/padreadonly.js b/src/node/hooks/express/padreadonly.js index 9a0a52bf..d60d3863 100644 --- a/src/node/hooks/express/padreadonly.js +++ b/src/node/hooks/express/padreadonly.js @@ -10,7 +10,6 @@ exports.expressCreateServer = function (hook_name, args, cb) { { var html; var padId; - var pad; async.series([ //translate the read only pad to a padId diff --git a/src/node/hooks/express/socketio.js b/src/node/hooks/express/socketio.js index adf15212..b70aa50e 100644 --- a/src/node/hooks/express/socketio.js +++ b/src/node/hooks/express/socketio.js @@ -1,4 +1,3 @@ -var log4js = require('log4js'); var settings = require('../../utils/Settings'); var socketio = require('socket.io'); var socketIORouter = require("../../handler/SocketIORouter"); diff --git a/src/node/hooks/express/static.js b/src/node/hooks/express/static.js index 7d654c1b..c6a22745 100644 --- a/src/node/hooks/express/static.js +++ b/src/node/hooks/express/static.js @@ -1,11 +1,8 @@ -var path = require('path'); var minify = require('../../utils/Minify'); var plugins = require("ep_etherpad-lite/static/js/pluginfw/plugins"); var CachingMiddleware = require('../../utils/caching_middleware'); var settings = require("../../utils/Settings"); var Yajsml = require('yajsml'); -var fs = require("fs"); -var ERR = require("async-stacktrace"); var _ = require("underscore"); exports.expressCreateServer = function (hook_name, args, cb) { diff --git a/src/node/hooks/express/swagger.js b/src/node/hooks/express/swagger.js index e8daa61c..55706a70 100644 --- a/src/node/hooks/express/swagger.js +++ b/src/node/hooks/express/swagger.js @@ -1,4 +1,3 @@ -var log4js = require('log4js'); var express = require('express'); var apiHandler = require('../../handler/APIHandler'); var apiCaller = require('./apicalls').apiCaller; diff --git a/src/node/hooks/express/webaccess.js b/src/node/hooks/express/webaccess.js index 6998853f..b798f2c7 100644 --- a/src/node/hooks/express/webaccess.js +++ b/src/node/hooks/express/webaccess.js @@ -2,7 +2,6 @@ var express = require('express'); var log4js = require('log4js'); var httpLogger = log4js.getLogger("http"); var settings = require('../../utils/Settings'); -var randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString; var hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks'); var ueberStore = require('../../db/SessionStore'); var stats = require('ep_etherpad-lite/node/stats') diff --git a/src/node/hooks/i18n.js b/src/node/hooks/i18n.js index 62631b93..67815659 100644 --- a/src/node/hooks/i18n.js +++ b/src/node/hooks/i18n.js @@ -1,7 +1,6 @@ var languages = require('languages4translatewiki') , fs = require('fs') , path = require('path') - , express = require('express') , _ = require('underscore') , npm = require('npm') , plugins = require('ep_etherpad-lite/static/js/pluginfw/plugins.js').plugins diff --git a/src/node/utils/Abiword.js b/src/node/utils/Abiword.js index 5f12bd97..1d9ac5d3 100644 --- a/src/node/utils/Abiword.js +++ b/src/node/utils/Abiword.js @@ -18,7 +18,6 @@ * limitations under the License. */ -var util = require('util'); var spawn = require('child_process').spawn; var async = require("async"); var settings = require("./Settings"); diff --git a/src/node/utils/ExportHelper.js b/src/node/utils/ExportHelper.js index 136896f0..3297c6d3 100644 --- a/src/node/utils/ExportHelper.js +++ b/src/node/utils/ExportHelper.js @@ -18,12 +18,7 @@ * limitations under the License. */ -var async = require("async"); var Changeset = require("ep_etherpad-lite/static/js/Changeset"); -var padManager = require("../db/PadManager"); -var ERR = require("async-stacktrace"); -var Security = require('ep_etherpad-lite/static/js/security'); -var hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks'); exports.getPadPlainText = function(pad, revNum){ var atext = ((revNum !== undefined) ? pad.getInternalRevisionAText(revNum) : pad.atext()); diff --git a/src/node/utils/ExportTxt.js b/src/node/utils/ExportTxt.js index f0b62743..a6bec4a5 100644 --- a/src/node/utils/ExportTxt.js +++ b/src/node/utils/ExportTxt.js @@ -22,9 +22,6 @@ var async = require("async"); var Changeset = require("ep_etherpad-lite/static/js/Changeset"); var padManager = require("../db/PadManager"); var ERR = require("async-stacktrace"); -var Security = require('ep_etherpad-lite/static/js/security'); -var hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks'); -var getPadPlainText = require('./ExportHelper').getPadPlainText; var _analyzeLine = require('./ExportHelper')._analyzeLine; // This is slightly different than the HTML method as it passes the output to getTXTFromAText @@ -82,7 +79,6 @@ function getTXTFromAtext(pad, atext, authorColors) var textLines = atext.text.slice(0, -1).split('\n'); var attribLines = Changeset.splitAttributionLines(atext.attribs, atext.text); - var tags = ['h1', 'h2', 'strong', 'em', 'u', 's']; var props = ['heading1', 'heading2', 'bold', 'italic', 'underline', 'strikethrough']; var anumMap = {}; var css = ""; @@ -110,7 +106,6 @@ function getTXTFromAtext(pad, atext, authorColors) // Just bold Bold and italics Just italics var taker = Changeset.stringIterator(text); var assem = Changeset.stringAssembler(); - var openTags = []; var idx = 0; @@ -250,7 +245,6 @@ function getTXTFromAtext(pad, atext, authorColors) // so we want to do something reasonable there. We also // want to deal gracefully with blank lines. // => keeps track of the parents level of indentation - var lists = []; // e.g. [[1,'bullet'], [3,'bullet'], ...] for (var i = 0; i < textLines.length; i++) { var line = _analyzeLine(textLines[i], attribLines[i], apool); diff --git a/src/node/utils/Minify.js b/src/node/utils/Minify.js index 132d63da..07b61c65 100644 --- a/src/node/utils/Minify.js +++ b/src/node/utils/Minify.js @@ -261,7 +261,6 @@ function getAceFile(callback) { // them into the file. async.forEach(founds, function (item, callback) { var filename = item.match(/"([^"]*)"/)[1]; - var request = require('request'); var baseURI = 'http://localhost:' + settings.port; var resourceURI = baseURI + path.normalize(path.join('/static/', filename)); diff --git a/src/node/utils/caching_middleware.js b/src/node/utils/caching_middleware.js index d30dc398..97134356 100644 --- a/src/node/utils/caching_middleware.js +++ b/src/node/utils/caching_middleware.js @@ -19,7 +19,6 @@ var Buffer = require('buffer').Buffer; var fs = require('fs'); var path = require('path'); var zlib = require('zlib'); -var util = require('util'); var settings = require('./Settings'); var semver = require('semver'); diff --git a/src/node/utils/toolbar.js b/src/node/utils/toolbar.js index e8d02dd6..a5d30f96 100644 --- a/src/node/utils/toolbar.js +++ b/src/node/utils/toolbar.js @@ -4,7 +4,6 @@ var _ = require("underscore") , tagAttributes , tag - , defaultButtons , Button , ButtonsGroup , Separator @@ -122,8 +121,7 @@ _.extend(SelectButton.prototype, Button.prototype, { }, select: function (attributes) { - var self = this - , options = []; + var options = []; _.each(this.options, function (opt) { var a = _.extend({ From 69bad8b30c9bc0a32cf4a13f75aaca3d90bf5058 Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Wed, 17 Dec 2014 01:10:20 +0100 Subject: [PATCH 197/263] fix typo in addSavedRevisions --- src/node/db/Pad.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node/db/Pad.js b/src/node/db/Pad.js index 2791334b..94049ff7 100644 --- a/src/node/db/Pad.js +++ b/src/node/db/Pad.js @@ -690,7 +690,7 @@ Pad.prototype.isPasswordProtected = function isPasswordProtected() { Pad.prototype.addSavedRevision = function addSavedRevision(revNum, savedById, label) { //if this revision is already saved, return silently for(var i in this.savedRevisions){ - if(this.savedRevisions.revNum === revNum){ + if(this.savedRevisions[i] && this.savedRevisions[i].revNum === revNum){ return; } } From ffdce760d5a1a38c9c96294087cbbfa76f3f76c2 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 21 Dec 2014 22:05:07 +0000 Subject: [PATCH 198/263] remove unused long paths from windows build --- bin/buildForWindows.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bin/buildForWindows.sh b/bin/buildForWindows.sh index a1e2c757..78441ba0 100755 --- a/bin/buildForWindows.sh +++ b/bin/buildForWindows.sh @@ -58,6 +58,8 @@ rm -rf .git/objects echo "remove windows jsdom-nocontextify/test folder" rm -rf /tmp/etherpad-lite-win/node_modules/ep_etherpad-lite/node_modules/jsdom-nocontextifiy/test/ rm -rf /tmp/etherpad-lite-win/src/node_modules/jsdom-nocontextifiy/test/ +rm -rf /tmp/etherpad-lite-win/src/node_modules/wd/node_modules/request/node_modules/form-data/node_modules/combined-stream/test +rm -rf /tmp/etherpad-lite-win/src/node_modules/nodemailer/node_modules/mailcomposer/node_modules/mimelib/node_modules/encoding/node_modules/iconv-lite/encodings/tables echo "create the zip..." cd /tmp From 745ef5db1cfec24bb1843f552cf50a8549a21437 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 21 Dec 2014 23:10:56 +0000 Subject: [PATCH 199/263] fix timeslider socket issue --- src/static/js/timeslider.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/static/js/timeslider.js b/src/static/js/timeslider.js index d00c5912..b3c10b8a 100644 --- a/src/static/js/timeslider.js +++ b/src/static/js/timeslider.js @@ -62,7 +62,7 @@ function init() { var resource = exports.baseURL.substring(1) + 'socket.io'; //build up the socket io connection - var socket = io.connect(url, {resource: resource}); + socket = io.connect(url, {resource: resource}); //send the ready message once we're connected socket.on('connect', function() From 3fe58068c0a80a334d1fbf43a1b00fe3ca4a0b3f Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 21 Dec 2014 23:16:00 +0000 Subject: [PATCH 200/263] more fixes --- src/static/js/broadcast_revisions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/static/js/broadcast_revisions.js b/src/static/js/broadcast_revisions.js index 9774dc3f..1980bdf3 100644 --- a/src/static/js/broadcast_revisions.js +++ b/src/static/js/broadcast_revisions.js @@ -48,7 +48,7 @@ function loadBroadcastRevisionsJS() }); } - var revisionInfo = {}; + revisionInfo = {}; revisionInfo.addChangeset = function(fromIndex, toIndex, changeset, backChangeset, timeDelta) { var startRevision = revisionInfo[fromIndex] || revisionInfo.createNew(fromIndex); From 4573796166b6b8afc5a0bcd9c82f987daba15464 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 21 Dec 2014 23:16:23 +0000 Subject: [PATCH 201/263] fix --- src/static/js/broadcast_revisions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/static/js/broadcast_revisions.js b/src/static/js/broadcast_revisions.js index 9774dc3f..1980bdf3 100644 --- a/src/static/js/broadcast_revisions.js +++ b/src/static/js/broadcast_revisions.js @@ -48,7 +48,7 @@ function loadBroadcastRevisionsJS() }); } - var revisionInfo = {}; + revisionInfo = {}; revisionInfo.addChangeset = function(fromIndex, toIndex, changeset, backChangeset, timeDelta) { var startRevision = revisionInfo[fromIndex] || revisionInfo.createNew(fromIndex); From ee3f3b80603f96f76f6d830464d59ef1e36dcd06 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 21 Dec 2014 23:42:56 +0000 Subject: [PATCH 202/263] move clean css back to fix minification on TS --- src/node/utils/Minify.js | 5 ++--- src/package.json | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/node/utils/Minify.js b/src/node/utils/Minify.js index 132d63da..e6b19542 100644 --- a/src/node/utils/Minify.js +++ b/src/node/utils/Minify.js @@ -23,7 +23,7 @@ var ERR = require("async-stacktrace"); var settings = require('./Settings'); var async = require('async'); var fs = require('fs'); -var CleanCSS = require('clean-css'); +var cleanCSS = require('clean-css'); var jsp = require("uglify-js").parser; var pro = require("uglify-js").uglify; var path = require('path'); @@ -411,8 +411,7 @@ function compressJS(values) function compressCSS(values) { var complete = values.join("\n"); - var cleanCSS = new CleanCSS({}).minify(complete); - return cleanCSS; + return cleanCSS.process(complete); } exports.minify = minify; diff --git a/src/package.json b/src/package.json index 3d5f5e24..03e892d5 100644 --- a/src/package.json +++ b/src/package.json @@ -20,7 +20,7 @@ "express" : ">3.1.0 <3.9.0", "async" : ">=0.9.0", "connect" : "2.7.x", - "clean-css" : ">=2.0.0", + "clean-css" : "0.3.2", "uglify-js" : ">=2.4.15", "formidable" : ">=1.0.15", "log4js" : ">=0.6.21", From b71fb3ad20a6d8225a2cb0fe3bac4d5dc9697df0 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 23 Dec 2014 02:38:50 +0000 Subject: [PATCH 203/263] fix issue with attributes --- src/static/js/linestylefilter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/static/js/linestylefilter.js b/src/static/js/linestylefilter.js index 757fac5a..675f19d0 100644 --- a/src/static/js/linestylefilter.js +++ b/src/static/js/linestylefilter.js @@ -60,7 +60,7 @@ linestylefilter.getLineStyleFilter = function(lineLength, aline, textAndClassFun // Plugin Hook to add more Attrib Classes hooks.aCallAll('aceAttribClasses', linestylefilter.ATTRIB_CLASSES, function(err, ATTRIB_CLASSES){ - if(ATTRIB_CLASSES){ + if(ATTRIB_CLASSES.length >= 1){ linestylefilter.ATTRIB_CLASSES = ATTRIB_CLASSES[0]; } }); From 80fa2c2868ebb0ad16c7512214dffa0781abbb1b Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 24 Dec 2014 02:45:21 +0000 Subject: [PATCH 204/263] move docs --- Makefile | 2 +- {tools => bin}/doc/LICENSE | 0 {tools => bin}/doc/README.md | 0 {tools => bin}/doc/generate.js | 0 {tools => bin}/doc/html.js | 0 {tools => bin}/doc/json.js | 0 {tools => bin}/doc/node_modules/.bin/marked | 0 {tools => bin}/doc/node_modules/marked/.npmignore | 0 {tools => bin}/doc/node_modules/marked/LICENSE | 0 {tools => bin}/doc/node_modules/marked/Makefile | 0 {tools => bin}/doc/node_modules/marked/README.md | 0 {tools => bin}/doc/node_modules/marked/bin/marked | 0 {tools => bin}/doc/node_modules/marked/index.js | 0 {tools => bin}/doc/node_modules/marked/lib/marked.js | 0 {tools => bin}/doc/node_modules/marked/man/marked.1 | 0 {tools => bin}/doc/node_modules/marked/package.json | 0 {tools => bin}/doc/package.json | 0 17 files changed, 1 insertion(+), 1 deletion(-) rename {tools => bin}/doc/LICENSE (100%) rename {tools => bin}/doc/README.md (100%) rename {tools => bin}/doc/generate.js (100%) rename {tools => bin}/doc/html.js (100%) rename {tools => bin}/doc/json.js (100%) rename {tools => bin}/doc/node_modules/.bin/marked (100%) rename {tools => bin}/doc/node_modules/marked/.npmignore (100%) rename {tools => bin}/doc/node_modules/marked/LICENSE (100%) rename {tools => bin}/doc/node_modules/marked/Makefile (100%) rename {tools => bin}/doc/node_modules/marked/README.md (100%) rename {tools => bin}/doc/node_modules/marked/bin/marked (100%) rename {tools => bin}/doc/node_modules/marked/index.js (100%) rename {tools => bin}/doc/node_modules/marked/lib/marked.js (100%) rename {tools => bin}/doc/node_modules/marked/man/marked.1 (100%) rename {tools => bin}/doc/node_modules/marked/package.json (100%) rename {tools => bin}/doc/package.json (100%) diff --git a/Makefile b/Makefile index b656d5d9..25fbf8e8 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ out/doc/assets/%: doc/assets/% out/doc/%.html: doc/%.md mkdir -p $(@D) - node tools/doc/generate.js --format=html --template=doc/template.html $< > $@ + node bin/doc/generate.js --format=html --template=doc/template.html $< > $@ ifeq ($(UNAME),Darwin) sed -i '' 's/__VERSION__/${VERSION}/' $@ else diff --git a/tools/doc/LICENSE b/bin/doc/LICENSE similarity index 100% rename from tools/doc/LICENSE rename to bin/doc/LICENSE diff --git a/tools/doc/README.md b/bin/doc/README.md similarity index 100% rename from tools/doc/README.md rename to bin/doc/README.md diff --git a/tools/doc/generate.js b/bin/doc/generate.js similarity index 100% rename from tools/doc/generate.js rename to bin/doc/generate.js diff --git a/tools/doc/html.js b/bin/doc/html.js similarity index 100% rename from tools/doc/html.js rename to bin/doc/html.js diff --git a/tools/doc/json.js b/bin/doc/json.js similarity index 100% rename from tools/doc/json.js rename to bin/doc/json.js diff --git a/tools/doc/node_modules/.bin/marked b/bin/doc/node_modules/.bin/marked similarity index 100% rename from tools/doc/node_modules/.bin/marked rename to bin/doc/node_modules/.bin/marked diff --git a/tools/doc/node_modules/marked/.npmignore b/bin/doc/node_modules/marked/.npmignore similarity index 100% rename from tools/doc/node_modules/marked/.npmignore rename to bin/doc/node_modules/marked/.npmignore diff --git a/tools/doc/node_modules/marked/LICENSE b/bin/doc/node_modules/marked/LICENSE similarity index 100% rename from tools/doc/node_modules/marked/LICENSE rename to bin/doc/node_modules/marked/LICENSE diff --git a/tools/doc/node_modules/marked/Makefile b/bin/doc/node_modules/marked/Makefile similarity index 100% rename from tools/doc/node_modules/marked/Makefile rename to bin/doc/node_modules/marked/Makefile diff --git a/tools/doc/node_modules/marked/README.md b/bin/doc/node_modules/marked/README.md similarity index 100% rename from tools/doc/node_modules/marked/README.md rename to bin/doc/node_modules/marked/README.md diff --git a/tools/doc/node_modules/marked/bin/marked b/bin/doc/node_modules/marked/bin/marked similarity index 100% rename from tools/doc/node_modules/marked/bin/marked rename to bin/doc/node_modules/marked/bin/marked diff --git a/tools/doc/node_modules/marked/index.js b/bin/doc/node_modules/marked/index.js similarity index 100% rename from tools/doc/node_modules/marked/index.js rename to bin/doc/node_modules/marked/index.js diff --git a/tools/doc/node_modules/marked/lib/marked.js b/bin/doc/node_modules/marked/lib/marked.js similarity index 100% rename from tools/doc/node_modules/marked/lib/marked.js rename to bin/doc/node_modules/marked/lib/marked.js diff --git a/tools/doc/node_modules/marked/man/marked.1 b/bin/doc/node_modules/marked/man/marked.1 similarity index 100% rename from tools/doc/node_modules/marked/man/marked.1 rename to bin/doc/node_modules/marked/man/marked.1 diff --git a/tools/doc/node_modules/marked/package.json b/bin/doc/node_modules/marked/package.json similarity index 100% rename from tools/doc/node_modules/marked/package.json rename to bin/doc/node_modules/marked/package.json diff --git a/tools/doc/package.json b/bin/doc/package.json similarity index 100% rename from tools/doc/package.json rename to bin/doc/package.json From c409495b5e3f0ea9e36a4a283c18950df4a2d9b8 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 24 Dec 2014 04:01:18 +0000 Subject: [PATCH 205/263] test for pad users --- tests/backend/specs/api/pad.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/backend/specs/api/pad.js b/tests/backend/specs/api/pad.js index 80f77220..e6b6de81 100644 --- a/tests/backend/specs/api/pad.js +++ b/tests/backend/specs/api/pad.js @@ -59,6 +59,7 @@ describe('Permission', function(){ -> getLastEdited(padID) -- Should be when pad was made -> setText(padId) -> getLastEdited(padID) -- Should be when setText was performed + -> padUsers(padID) -- Should be when setText was performed */ describe('deletePad', function(){ @@ -252,6 +253,18 @@ describe('getLastEdited', function(){ }); }) +describe('padUsers', function(){ + it('gets User Count of a Pad', function(done) { + api.get(endPoint('padUsers')+"&padID="+testPadId) + .expect(function(res){ + if(res.body.data.padUsers.length !== 0) throw new Error("Incorrect Pad Users") + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + + var endPoint = function(point){ return '/api/'+apiVersion+'/'+point+'?apikey='+apiKey; From 88ab74b1e9671eaeffc695c84b332202bb195127 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 24 Dec 2014 17:54:13 +0000 Subject: [PATCH 206/263] fix flowing of items in editbar --- src/static/css/pad.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/static/css/pad.css b/src/static/css/pad.css index 7ecf8e22..ba066f18 100644 --- a/src/static/css/pad.css +++ b/src/static/css/pad.css @@ -74,6 +74,7 @@ a img { -moz-user-select: none; -ms-user-select: none; user-select: none; + height:32px; } .toolbar ul li.separator { border: inherit; @@ -81,7 +82,7 @@ a img { visibility: hidden; width: 0px; padding: 5px; - height:20px; + height:22px; } .toolbar ul li a:hover { text-decoration: none; From 10d92b6bf16fc05e052a5653bb11a0e98dd7be33 Mon Sep 17 00:00:00 2001 From: John McLear Date: Fri, 26 Dec 2014 23:42:00 +0000 Subject: [PATCH 207/263] slightly better attribute selection logic but still pretty b0rked --- src/static/js/ace2_inner.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 5d337735..4fd15b5c 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -2313,6 +2313,25 @@ function Ace2Inner(){ function getAttributeOnSelection(attributeName){ if (!(rep.selStart && rep.selEnd)) return; + + // get the previous/next characters formatting when we have nothing selected + // To fix this we just change the focus area, we don't actually check anything yet. + if(rep.selStart[1] == rep.selEnd[1]){ + // if we're at the beginning of a line bump end forward so we get the right attribute + if(rep.selStart[1] == 0 && rep.selEnd[1] == 0){ + rep.selEnd[1] = 1; + } + if(rep.selStart[1] < 0){ + rep.selStart[1] = 0; + } + var line = rep.lines.atIndex(rep.selStart[0]); + // if we're at the end of the line bmp the start back 1 so we get hte attribute + if(rep.selEnd[1] == line.text.length){ + rep.selStart[1] = rep.selStart[1] -1; + } + } + + // Do the detection var selectionAllHasIt = true; var withIt = Changeset.makeAttribsString('+', [ [attributeName, 'true'] From 9d66a446dc8c643213ee0bfcbd427425e01a18dc Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 27 Dec 2014 13:18:58 +0100 Subject: [PATCH 208/263] support indentation up to 16 levels and dont go back afterwards --- src/static/css/iframe_editor.css | 93 +++++++++++++++++++++++++++++--- src/static/js/ace2_inner.js | 14 ++--- 2 files changed, 92 insertions(+), 15 deletions(-) diff --git a/src/static/css/iframe_editor.css b/src/static/css/iframe_editor.css index b88db7a1..575ee1a6 100644 --- a/src/static/css/iframe_editor.css +++ b/src/static/css/iframe_editor.css @@ -28,6 +28,14 @@ ul.list-bullet5 { margin-left: 7.5em; } ul.list-bullet6 { margin-left: 9em; } ul.list-bullet7 { margin-left: 10.5em; } ul.list-bullet8 { margin-left: 12em; } +ul.list-bullet9 { margin-left: 13.5em; } +ul.list-bullet10 { margin-left: 15em; } +ul.list-bullet11 { margin-left: 16.5em; } +ul.list-bullet12 { margin-left: 18em; } +ul.list-bullet13 { margin-left: 19.5em; } +ul.list-bullet14 { margin-left: 21em; } +ul.list-bullet15 { margin-left: 22.5em; } +ul.list-bullet16 { margin-left: 24em; } ul { list-style-type: disc; } ul.list-bullet1 { list-style-type: disc; } @@ -38,6 +46,14 @@ ul.list-bullet5 { list-style-type: circle; } ul.list-bullet6 { list-style-type: square; } ul.list-bullet7 { list-style-type: disc; } ul.list-bullet8 { list-style-type: circle; } +ul.list-bullet9 { list-style-type: disc; } +ul.list-bullet10 { list-style-type: circle; } +ul.list-bullet11 { list-style-type: square; } +ul.list-bullet12 { list-style-type: disc; } +ul.list-bullet13 { list-style-type: circle; } +ul.list-bullet14 { list-style-type: square; } +ul.list-bullet15 { list-style-type: disc; } +ul.list-bullet16 { list-style-type: circle; } ul.list-indent1 { margin-left: 1.5em; } ul.list-indent2 { margin-left: 3em; } @@ -47,15 +63,19 @@ ul.list-indent5 { margin-left: 7.5em; } ul.list-indent6 { margin-left: 9em; } ul.list-indent7 { margin-left: 10.5em; } ul.list-indent8 { margin-left: 12em; } +ul.list-indent9 { margin-left: 13.5em; } +ul.list-indent10 { margin-left: 15em; } +ul.list-indent11 { margin-left: 16.5em; } +ul.list-indent12 { margin-left: 18em; } +ul.list-indent13 { margin-left: 19.5em; } +ul.list-indent14 { margin-left: 21em; } +ul.list-indent15 { margin-left: 22.5em; } +ul.list-indent16 { margin-left: 24em; } -ul.list-indent1 { list-style-type: none; } -ul.list-indent2 { list-style-type: none; } -ul.list-indent3 { list-style-type: none; } -ul.list-indent4 { list-style-type: none; } -ul.list-indent5 { list-style-type: none; } -ul.list-indent6 { list-style-type: none; } -ul.list-indent7 { list-style-type: none; } -ul.list-indent8 { list-style-type: none; } +ul.list-indent1, ul.list-indent2, ul.list-indent3, ul.list-indent4, ul.list-indent5, +ul.list-indent6, ul.list-indent7, ul.list-indent8, ul.list-indent9, ul.list-indent10, +ul.list-indent11, ul.list-indent12, ul.list-indent13, +ul.list-indent14, ul.list-indent15, ul.list-indent16 { list-style-type: none; } body { margin: 0; @@ -184,6 +204,14 @@ ol.list-number5{ text-indent: 40px; } ol.list-number6{ text-indent: 50px; } ol.list-number7{ text-indent: 60px; } ol.list-number8{ text-indent: 70px; } +ol.list-number9{ text-indent: 80px; } +ol.list-number10{ text-indent: 90px; } +ol.list-number11{ text-indent: 100px; } +ol.list-number12{ text-indent: 110px; } +ol.list-number13{ text-indent: 120px; } +ol.list-number14{ text-indent: 130px; } +ol.list-number15{ text-indent: 140px; } +ol.list-number16{ text-indent: 150px; } /* Add styling to the first item in a list */ @@ -195,6 +223,14 @@ ol.list-number8{ text-indent: 70px; } .list-start-number6 { counter-reset: sixth; } .list-start-number7 { counter-reset: seventh; } .list-start-number8 { counter-reset: eighth; } +.list-start-number9 { counter-reset: ninth; } +.list-start-number10 { counter-reset: tenth; } +.list-start-number11 { counter-reset: eleventh; } +.list-start-number12 { counter-reset: twelth; } +.list-start-number13 { counter-reset: thirteenth; } +.list-start-number14 { counter-reset: fourteenth; } +.list-start-number15 { counter-reset: fifteenth; } +.list-start-number16 { counter-reset: sixteenth; } /* The behavior for incrementing and the prefix */ .list-number1 li:before { @@ -236,3 +272,44 @@ ol.list-number8{ text-indent: 70px; } content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(eighth) ". " ; counter-increment: eighth 1; } + +.list-number9 li:before { + content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(eighth) "." counter(ninth) ". "; + counter-increment: ninth 1; +} + +.list-number10 li:before { + content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(eighth) "." counter(ninth) "." counter(tenth) ". "; + counter-increment: tenth 1; +} + +.list-number11 li:before { + content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(eighth) "." counter(ninth) "." counter(tenth) "." counter(eleventh) ". "; + counter-increment: eleventh 1; +} + +.list-number12 li:before { + content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(eighth) "." counter(ninth) "." counter(tenth) "." counter(eleventh) "." counter(twelth) ". "; + counter-increment: twelth 1; +} + +.list-number13 li:before { + content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(eighth) "." counter(ninth) "." counter(tenth) "." counter(eleventh) "." counter(twelth) "." counter(thirteenth) ". "; + counter-increment: thirteenth 1; +} + +.list-number14 li:before { + content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(eighth) "." counter(ninth) "." counter(tenth) "." counter(eleventh) "." counter(twelth) "." counter(thirteenth) "." counter(fourteenth) ". "; + counter-increment: fourteenth 1; +} + +.list-number15 li:before { + content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(eighth) "." counter(ninth) "." counter(tenth) "." counter(eleventh) "." counter(twelth) "." counter(thirteenth) "." counter(fourteenth) "." counter(fifteenth) ". "; + counter-increment: fifteenth 1; +} + +.list-number16 li:before { + content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(eighth) "." counter(ninth) "." counter(tenth) "." counter(eleventh) "." counter(twelth) "." counter(thirteenth) "." counter(fourteenth) "." counter(fifteenth) "." counter(sixteenth) ". "; + counter-increment: fixteenth 1; +} + diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 4fd15b5c..f1fc1160 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -57,7 +57,7 @@ function Ace2Inner(){ var isSetUp = false; var THE_TAB = ' '; //4 - var MAX_LIST_LEVEL = 8; + var MAX_LIST_LEVEL = 16; var LINE_NUMBER_PADDING_RIGHT = 4; var LINE_NUMBER_PADDING_LEFT = 4; @@ -3357,7 +3357,7 @@ function Ace2Inner(){ if (listType) { var text = rep.lines.atIndex(lineNum).text; - listType = /([a-z]+)([12345678])/.exec(listType); + listType = /([a-z]+)([0-9]+)/.exec(listType); var type = listType[1]; var level = Number(listType[2]); @@ -3409,7 +3409,7 @@ function Ace2Inner(){ var level = 0; if (listType) { - listType = /([a-z]+)([12345678])/.exec(listType); + listType = /([a-z]+)([0-9]+)/.exec(listType); if (listType) { t = listType[1]; @@ -5094,7 +5094,7 @@ function Ace2Inner(){ { return null; } - type = /([a-z]+)[12345678]/.exec(type); + type = /([a-z]+)[0-9+]/.exec(type); if(type[1] == "indent") { return null; @@ -5103,7 +5103,7 @@ function Ace2Inner(){ //2-find the first line of the list while(lineNum-1 >= 0 && (type=getLineListType(lineNum-1))) { - type = /([a-z]+)[12345678]/.exec(type); + type = /([a-z]+)[0-9+]/.exec(type); if(type[1] == "indent") break; lineNum--; @@ -5123,7 +5123,7 @@ function Ace2Inner(){ while(listType = getLineListType(line)) { //apply new num - listType = /([a-z]+)([12345678])/.exec(listType); + listType = /([a-z]+)([0-9+])/.exec(listType); curLevel = Number(listType[2]); if(isNaN(curLevel) || listType[0] == "indent") { @@ -5191,7 +5191,7 @@ function Ace2Inner(){ { var t = ''; var level = 0; - var listType = /([a-z]+)([12345678])/.exec(getLineListType(n)); + var listType = /([a-z]+)([0-9]+)/.exec(getLineListType(n)); if (listType) { t = listType[1]; From 6ac99ab03fc78ff418fd631fa9a630dae2f6135a Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 27 Dec 2014 13:27:07 +0100 Subject: [PATCH 209/263] support for export --- src/node/utils/ExportHtml.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/node/utils/ExportHtml.js b/src/node/utils/ExportHtml.js index 693cf9cf..85d5e7a2 100644 --- a/src/node/utils/ExportHtml.js +++ b/src/node/utils/ExportHtml.js @@ -456,7 +456,15 @@ exports.getPadHTMLDocument = function (padId, revNum, noDocType, callback) 'ol ol ol ol ol { list-style-type: lower-latin; }' + 'ol ol ol ol ol ol{ list-style-type: lower-roman; }' + 'ol ol ol ol ol ol ol { list-style-type: decimal; }' + - 'ol ol ol ol ol ol ol ol{ list-style-type: lower-latin; }' + + 'ol ol ol ol ol ol ol ol{ list-style-type: lower-latin; }' + + 'ol ol ol ol ol ol ol ol ol { list-style-type: decimal; }' + + 'ol ol ol ol ol ol ol ol ol ol { list-style-type: lower-latin; }' + + 'ol ol ol ol ol ol ol ol ol ol ol { list-style-type: lower-roman; }' + + 'ol ol ol ol ol ol ol ol ol ol ol ol { list-style-type: decimal; }' + + 'ol ol ol ol ol ol ol ol ol ol ol ol ol { list-style-type: lower-latin; }' + + 'ol ol ol ol ol ol ol ol ol ol ol ol ol ol{ list-style-type: lower-roman; }' + + 'ol ol ol ol ol ol ol ol ol ol ol ol ol ol ol { list-style-type: decimal; }' + + 'ol ol ol ol ol ol ol ol ol ol ol ol ol ol ol ol{ list-style-type: lower-latin; }' + stylesForExportCSS + '\n' + '\n') + ''; From ae22332f71e29c014f02b801c371b31d9af3c09e Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 27 Dec 2014 14:08:45 +0100 Subject: [PATCH 210/263] removing dokuwiki --- src/locales/en.json | 1 - src/node/handler/ExportHandler.js | 21 -- src/node/hooks/express/importexport.js | 2 +- src/node/utils/ExportDokuWiki.js | 350 ------------------------- src/static/css/pad.css | 3 - src/static/js/pad_impexp.js | 1 - src/templates/pad.html | 1 - src/templates/timeslider.html | 1 - 8 files changed, 1 insertion(+), 379 deletions(-) delete mode 100644 src/node/utils/ExportDokuWiki.js diff --git a/src/locales/en.json b/src/locales/en.json index 9a25b18c..9a5fe45f 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -49,7 +49,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "You only can import from plain text or HTML formats. For more advanced import features please install abiword.", "pad.modals.connected": "Connected.", diff --git a/src/node/handler/ExportHandler.js b/src/node/handler/ExportHandler.js index 5bedcce2..bc03f532 100644 --- a/src/node/handler/ExportHandler.js +++ b/src/node/handler/ExportHandler.js @@ -21,7 +21,6 @@ var ERR = require("async-stacktrace"); var exporthtml = require("../utils/ExportHtml"); var exporttxt = require("../utils/ExportTxt"); -var exportdokuwiki = require("../utils/ExportDokuWiki"); var padManager = require("../db/PadManager"); var async = require("async"); var fs = require("fs"); @@ -129,26 +128,6 @@ exports.doExport = function(req, res, padId, type) if(err && err != "stop") ERR(err); }) } - else if(type == 'dokuwiki') - { - var randNum; - var srcFile, destFile; - - async.series([ - //render the dokuwiki document - function(callback) - { - exportdokuwiki.getPadDokuWikiDocument(padId, req.params.rev, function(err, dokuwiki) - { - res.send(dokuwiki); - callback("stop"); - }); - }, - ], function(err) - { - if(err && err != "stop") throw err; - }); - } else { var html; diff --git a/src/node/hooks/express/importexport.js b/src/node/hooks/express/importexport.js index f5a3e5a1..378e8865 100644 --- a/src/node/hooks/express/importexport.js +++ b/src/node/hooks/express/importexport.js @@ -5,7 +5,7 @@ var importHandler = require('../../handler/ImportHandler'); exports.expressCreateServer = function (hook_name, args, cb) { args.app.get('/p/:pad/:rev?/export/:type', function(req, res, next) { - var types = ["pdf", "doc", "txt", "html", "odt", "dokuwiki"]; + var types = ["pdf", "doc", "txt", "html", "odt"]; //send a 404 if we don't support this filetype if (types.indexOf(req.params.type) == -1) { next(); diff --git a/src/node/utils/ExportDokuWiki.js b/src/node/utils/ExportDokuWiki.js deleted file mode 100644 index f5d2d177..00000000 --- a/src/node/utils/ExportDokuWiki.js +++ /dev/null @@ -1,350 +0,0 @@ -/** - * Copyright 2011 Adrian Lang - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS-IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -var async = require("async"); - -var Changeset = require("ep_etherpad-lite/static/js/Changeset"); -var padManager = require("../db/PadManager"); - -function getPadDokuWiki(pad, revNum, callback) -{ - var atext = pad.atext; - var dokuwiki; - async.waterfall([ - // fetch revision atext - - - function (callback) - { - if (revNum != undefined) - { - pad.getInternalRevisionAText(revNum, function (err, revisionAtext) - { - atext = revisionAtext; - callback(err); - }); - } - else - { - callback(null); - } - }, - - // convert atext to dokuwiki text - - function (callback) - { - dokuwiki = getDokuWikiFromAtext(pad, atext); - callback(null); - }], - // run final callback - - - function (err) - { - callback(err, dokuwiki); - }); -} - -function getDokuWikiFromAtext(pad, atext) -{ - var apool = pad.apool(); - var textLines = atext.text.slice(0, -1).split('\n'); - var attribLines = Changeset.splitAttributionLines(atext.attribs, atext.text); - - var tags = ['======', '=====', '**', '//', '__', 'del>']; - var props = ['heading1', 'heading2', 'bold', 'italic', 'underline', 'strikethrough']; - var anumMap = {}; - - props.forEach(function (propName, i) - { - var propTrueNum = apool.putAttrib([propName, true], true); - if (propTrueNum >= 0) - { - anumMap[propTrueNum] = i; - } - }); - - function getLineDokuWiki(text, attribs) - { - var propVals = [false, false, false]; - var ENTER = 1; - var STAY = 2; - var LEAVE = 0; - - // Use order of tags (b/i/u) as order of nesting, for simplicity - // and decent nesting. For example, - // Just bold Bold and italics Just italics - // becomes - // Just bold Bold and italics Just italics - var taker = Changeset.stringIterator(text); - var assem = Changeset.stringAssembler(); - - function emitOpenTag(i) - { - if (tags[i].indexOf('>') !== -1) { - assem.append('<'); - } - assem.append(tags[i]); - } - - function emitCloseTag(i) - { - if (tags[i].indexOf('>') !== -1) { - assem.append(' bold, etc. - if (!propVals[i]) - { - propVals[i] = ENTER; - propChanged = true; - } - else - { - propVals[i] = STAY; - } - } - }); - for (var i = 0; i < propVals.length; i++) - { - if (propVals[i] === true) - { - propVals[i] = LEAVE; - propChanged = true; - } - else if (propVals[i] === STAY) - { - propVals[i] = true; // set it back - } - } - // now each member of propVal is in {false,LEAVE,ENTER,true} - // according to what happens at start of span - if (propChanged) - { - // leaving bold (e.g.) also leaves italics, etc. - var left = false; - for (var i = 0; i < propVals.length; i++) - { - var v = propVals[i]; - if (!left) - { - if (v === LEAVE) - { - left = true; - } - } - else - { - if (v === true) - { - propVals[i] = STAY; // tag will be closed and re-opened - } - } - } - - for (var i = propVals.length - 1; i >= 0; i--) - { - if (propVals[i] === LEAVE) - { - emitCloseTag(i); - propVals[i] = false; - } - else if (propVals[i] === STAY) - { - emitCloseTag(i); - } - } - for (var i = 0; i < propVals.length; i++) - { - if (propVals[i] === ENTER || propVals[i] === STAY) - { - emitOpenTag(i); - propVals[i] = true; - } - } - // propVals is now all {true,false} again - } // end if (propChanged) - var chars = o.chars; - if (o.lines) - { - chars--; // exclude newline at end of line, if present - } - var s = taker.take(chars); - - assem.append(_escapeDokuWiki(s)); - } // end iteration over spans in line - for (var i = propVals.length - 1; i >= 0; i--) - { - if (propVals[i]) - { - emitCloseTag(i); - propVals[i] = false; - } - } - } // end processNextChars - if (urls) - { - urls.forEach(function (urlData) - { - var startIndex = urlData[0]; - var url = urlData[1]; - var urlLength = url.length; - processNextChars(startIndex - idx); - assem.append('[['); - - // Do not use processNextChars since a link does not contain syntax and - // needs no escaping - var iter = Changeset.opIterator(Changeset.subattribution(attribs, idx, idx + urlLength)); - idx += urlLength; - assem.append(taker.take(iter.next().chars)); - - assem.append(']]'); - }); - } - processNextChars(text.length - idx); - - return assem.toString() + "\n"; - } // end getLineDokuWiki - var pieces = []; - - for (var i = 0; i < textLines.length; i++) - { - var line = _analyzeLine(textLines[i], attribLines[i], apool); - var lineContent = getLineDokuWiki(line.text, line.aline); - - if (line.listLevel && lineContent) - { - if (line.listTypeName == "number") - { - pieces.push(new Array(line.listLevel + 1).join(' ') + ' - '); - } else { - pieces.push(new Array(line.listLevel + 1).join(' ') + '* '); - } - } - pieces.push(lineContent); - } - - return pieces.join(''); -} - -function _analyzeLine(text, aline, apool) -{ - var line = {}; - - // identify list - var lineMarker = 0; - line.listLevel = 0; - if (aline) - { - var opIter = Changeset.opIterator(aline); - if (opIter.hasNext()) - { - var listType = Changeset.opAttributeValue(opIter.next(), 'list', apool); - if (listType) - { - lineMarker = 1; - listType = /([a-z]+)([12345678])/.exec(listType); - if (listType) - { - line.listTypeName = listType[1]; - line.listLevel = Number(listType[2]); - } - } - } - } - if (lineMarker) - { - line.text = text.substring(1); - line.aline = Changeset.subattribution(aline, 1); - } - else - { - line.text = text; - line.aline = aline; - } - - return line; -} - -exports.getPadDokuWikiDocument = function (padId, revNum, callback) -{ - padManager.getPad(padId, function (err, pad) - { - if (err) - { - callback(err); - return; - } - - getPadDokuWiki(pad, revNum, callback); - }); -}; - -function _escapeDokuWiki(s) -{ - s = s.replace(/(\/\/|\*\*|__)/g, '%%$1%%'); - return s; -} - -// copied from ACE -var _REGEX_WORDCHAR = /[\u0030-\u0039\u0041-\u005A\u0061-\u007A\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\u1FFF\u3040-\u9FFF\uF900-\uFDFF\uFE70-\uFEFE\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFDC]/; -var _REGEX_SPACE = /\s/; -var _REGEX_URLCHAR = new RegExp('(' + /[-:@a-zA-Z0-9_.,~%+\/\\?=&#;()$]/.source + '|' + _REGEX_WORDCHAR.source + ')'); -var _REGEX_URL = new RegExp(/(?:(?:https?|s?ftp|ftps|file|smb|afp|nfs|(x-)?man|gopher|txmt):\/\/|mailto:)/.source + _REGEX_URLCHAR.source + '*(?![:.,;])' + _REGEX_URLCHAR.source, 'g'); - -// returns null if no URLs, or [[startIndex1, url1], [startIndex2, url2], ...] - - -function _findURLs(text) -{ - _REGEX_URL.lastIndex = 0; - var urls = null; - var execResult; - while ((execResult = _REGEX_URL.exec(text))) - { - urls = (urls || []); - var startIndex = execResult.index; - var url = execResult[0]; - urls.push([startIndex, url]); - } - - return urls; -} diff --git a/src/static/css/pad.css b/src/static/css/pad.css index ba066f18..c1035e8d 100644 --- a/src/static/css/pad.css +++ b/src/static/css/pad.css @@ -692,9 +692,6 @@ table#otheruserstable { #exportopena:before { content: "\e805"; } -#exportdokuwikia:before { - content: "\e805"; -} /* hidden element */ #importstatusball, diff --git a/src/static/js/pad_impexp.js b/src/static/js/pad_impexp.js index 1454bb31..20cae2a0 100644 --- a/src/static/js/pad_impexp.js +++ b/src/static/js/pad_impexp.js @@ -199,7 +199,6 @@ var padimpexp = (function() // build the export links $("#exporthtmla").attr("href", pad_root_path + "/export/html"); $("#exportplaina").attr("href", pad_root_path + "/export/txt"); - $("#exportdokuwikia").attr("href", pad_root_path + "/export/dokuwiki"); // activate action to import in the form $("#importform").attr('action', pad_root_url + "/import"); diff --git a/src/templates/pad.html b/src/templates/pad.html index c20530ef..2dd66aa9 100644 --- a/src/templates/pad.html +++ b/src/templates/pad.html @@ -208,7 +208,6 @@
-
<% e.end_block(); %>
diff --git a/src/templates/timeslider.html b/src/templates/timeslider.html index 1fab0279..a619c702 100644 --- a/src/templates/timeslider.html +++ b/src/templates/timeslider.html @@ -186,7 +186,6 @@
-
From 444bbf4cbc503148ebc4a80152093adb3c55c7a8 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 27 Dec 2014 14:19:31 +0100 Subject: [PATCH 211/263] remove trailing enter from apikey --- tests/backend/specs/api/pad.js | 1 + tests/backend/specs/api/sessionsAndGroups.js | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/backend/specs/api/pad.js b/tests/backend/specs/api/pad.js index e6b6de81..700c498f 100644 --- a/tests/backend/specs/api/pad.js +++ b/tests/backend/specs/api/pad.js @@ -7,6 +7,7 @@ var assert = require('assert') var filePath = path.join(__dirname, '../../../../APIKEY.txt'); var apiKey = fs.readFileSync(filePath, {encoding: 'utf-8'}); +apiKey = apiKey.replace(/\n$/, ""); var apiVersion = 1; var testPadId = makeid(); var lastEdited = ""; diff --git a/tests/backend/specs/api/sessionsAndGroups.js b/tests/backend/specs/api/sessionsAndGroups.js index 86ba454a..4742852f 100644 --- a/tests/backend/specs/api/sessionsAndGroups.js +++ b/tests/backend/specs/api/sessionsAndGroups.js @@ -7,6 +7,7 @@ var assert = require('assert') var filePath = path.join(__dirname, '../../../../APIKEY.txt'); var apiKey = fs.readFileSync(filePath, {encoding: 'utf-8'}); +apiKey = apiKey.replace(/\n$/, ""); var apiVersion = 1; var testPadId = makeid(); var groupID = ""; From 40c7ee9df911c98474b80a3030bc50bceba95d2c Mon Sep 17 00:00:00 2001 From: Gared Date: Sat, 27 Dec 2014 14:26:05 +0100 Subject: [PATCH 212/263] Prevent server crash if author is not set --- src/node/handler/PadMessageHandler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index bc8f5d62..7cdefc65 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -1020,7 +1020,7 @@ function handleClientReady(client, message) { authorManager.getAuthor(authorId, function(err, author) { - if(ERR(err, callback)) return; + if(ERR(err, callback) || !author) return; historicalAuthorData[authorId] = {name: author.name, colorId: author.colorId}; // Filter author attribs (e.g. don't send author's pads to all clients) callback(); }); From a151c207c2d14b7900191737e601ea883db8c423 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 27 Dec 2014 15:08:54 +0100 Subject: [PATCH 213/263] set prefs and get prefs --- src/static/js/pad.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/static/js/pad.js b/src/static/js/pad.js index 89ebfa76..846b54ad 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.js @@ -525,6 +525,15 @@ var pad = { if(padcookie.getPref("showAuthorshipColors") == false){ pad.changeViewOption('showAuthorColors', false); } + if(padcookie.getPref("showLineNumbers") == false){ + pad.changeViewOption('showLineNumbers', false); + } + if(padcookie.getPref("rtlIsTrue") == true){ + pad.changeViewOption('rtlIsTrue', true); + } + if(padcookie.getPref("useMonospaceFont") == true){ + pad.changeViewOption('useMonospaceFont', true); + } hooks.aCallAll("postAceInit", {ace: padeditor.ace, pad: pad}); } }, @@ -575,6 +584,7 @@ var pad = { for (var k in opts.view) { pad.padOptions.view[k] = opts.view[k]; + padcookie.setPref(k, opts.view[k]); } padeditor.setViewOptions(pad.padOptions.view); } @@ -728,10 +738,8 @@ var pad = { }, handleIsFullyConnected: function(isConnected, isInitialConnect) { - pad.determineChatVisibility(isConnected && !isInitialConnect); pad.determineAuthorshipColorsVisibility(); - }, determineChatVisibility: function(asNowConnectedFeedback){ var chatVisCookie = padcookie.getPref('chatAlwaysVisible'); From a63880dcb13579977eab03d00c3d74366b5c6d22 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Sat, 27 Dec 2014 16:15:20 +0100 Subject: [PATCH 214/263] Fix AttributeManager#removeAttributeOnLine: Only remove a single attrib --- src/static/js/AttributeManager.js | 35 +++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/src/static/js/AttributeManager.js b/src/static/js/AttributeManager.js index 2d523f6a..b1f6b421 100644 --- a/src/static/js/AttributeManager.js +++ b/src/static/js/AttributeManager.js @@ -96,6 +96,32 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ return ''; }, + /* + Gets all attributes on a line + @param lineNum: the number of the line to set the attribute for + */ + getAttributesOnLine: function(lineNum){ + // get attributes of first char of line + var aline = this.rep.alines[lineNum]; + var attributes = [] + if (aline) + { + var opIter = Changeset.opIterator(aline) + , op + if (opIter.hasNext()) + { + op = opIter.next() + if(!op.attribs) return [] + + Changeset.eachAttribNumber(op.attribs, function(n) { + attributes.push([this.rep.apool.getAttribKey(n), this.rep.apool.getAttribValue(n)]) + }.bind(this)) + return attributes; + } + } + return []; + }, + /* Sets a specified attribute on a line @param lineNum: the number of the line to set the attribute for @@ -134,14 +160,19 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ */ removeAttributeOnLine: function(lineNum, attributeName, attributeValue){ - var loc = [0,0]; var builder = Changeset.builder(this.rep.lines.totalWidth()); var hasMarker = this.lineHasMarker(lineNum); + var attribs + + attribs = this.getAttributesOnLine(lineNum).map(function(attrib) { + if(attrib[0] === attributeName) return [attributeName, null] + return attrib + }) if(hasMarker){ ChangesetUtils.buildKeepRange(this.rep, builder, loc, (loc = [lineNum, 0])); - ChangesetUtils.buildRemoveRange(this.rep, builder, loc, (loc = [lineNum, 1])); + ChangesetUtils.buildKeepRange(this.rep, builder, loc, (loc = [lineNum, 1]), attribs, this.rep.apool); } return this.applyChangeset(builder); From 07a6702363a2b3cb195422cbc618de221a83b278 Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Sat, 27 Dec 2014 17:56:20 +0100 Subject: [PATCH 215/263] Revert "be more strict in checkRep" This reverts commit 22803da42f4f8c6aeccc15d0a916bae6332a8652. --- src/static/js/Changeset.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/static/js/Changeset.js b/src/static/js/Changeset.js index 4d5487f8..366ad15f 100644 --- a/src/static/js/Changeset.js +++ b/src/static/js/Changeset.js @@ -255,20 +255,20 @@ exports.checkRep = function (cs) { var o = iter.next(); switch (o.opcode) { case '=': - exports.assert(o.chars >= o.lines, o.chars, " chars and ", o.lines, " lines in op ",cs); oldPos += o.chars; calcNewLen += o.chars; break; case '-': - exports.assert(o.chars >= o.lines, o.chars, " chars and ", o.lines, " lines in op ",cs); oldPos += o.chars; exports.assert(oldPos < oldLen, oldPos, " >= ", oldLen, " in ", cs); break; case '+': - exports.assert(o.chars >= o.lines, o.chars, " chars and ", o.lines, " lines in op ",cs); - calcNewLen += o.chars; - exports.assert(calcNewLen < newLen, calcNewLen, " >= ", newLen, " in ", cs); - break; + { + calcNewLen += o.chars; + numInserted += o.chars; + exports.assert(calcNewLen < newLen, calcNewLen, " >= ", newLen, " in ", cs); + break; + } } assem.append(o); } From ddd6a8ebcef551b7b261856c031b0a68b416128d Mon Sep 17 00:00:00 2001 From: Siebrand Mazeland Date: Sat, 27 Dec 2014 17:59:55 +0100 Subject: [PATCH 216/263] Localisation updates from https://translatewiki.net. --- src/locales/af.json | 1 - src/locales/ar.json | 1 - src/locales/ast.json | 1 - src/locales/az.json | 1 - src/locales/azb.json | 22 +++++++++++++++++++--- src/locales/bcc.json | 1 - src/locales/be-tarask.json | 9 ++++----- src/locales/bn.json | 6 +++--- src/locales/br.json | 9 ++++----- src/locales/ca.json | 6 +++--- src/locales/cs.json | 1 - src/locales/da.json | 9 ++++----- src/locales/de.json | 3 +-- src/locales/diq.json | 11 +++++------ src/locales/dsb.json | 1 - src/locales/el.json | 13 ++++++------- src/locales/es.json | 27 +++++++++++++-------------- src/locales/et.json | 1 - src/locales/eu.json | 1 - src/locales/fa.json | 9 ++++----- src/locales/fi.json | 12 ++++++------ src/locales/fo.json | 1 - src/locales/fr.json | 6 +++--- src/locales/gl.json | 12 ++++++------ src/locales/he.json | 3 +-- src/locales/hrx.json | 1 - src/locales/hsb.json | 1 - src/locales/hu.json | 9 ++++----- src/locales/ia.json | 9 ++++----- src/locales/it.json | 3 +-- src/locales/ja.json | 1 - src/locales/km.json | 1 - src/locales/ko.json | 9 ++++----- src/locales/ksh.json | 9 ++++----- src/locales/lrc.json | 1 - src/locales/lt.json | 1 - src/locales/lv.json | 1 - src/locales/map-bms.json | 1 - src/locales/mk.json | 3 +-- src/locales/ml.json | 9 ++++----- src/locales/mr.json | 1 - src/locales/ms.json | 9 ++++----- src/locales/nap.json | 9 ++++----- src/locales/nb.json | 9 ++++----- src/locales/nds.json | 1 - src/locales/ne.json | 1 - src/locales/nl.json | 13 +++++++------ src/locales/nn.json | 1 - src/locales/oc.json | 1 - src/locales/os.json | 1 - src/locales/pa.json | 1 - src/locales/pl.json | 6 +++--- src/locales/ps.json | 1 - src/locales/pt-br.json | 13 +++++++------ src/locales/pt.json | 3 +-- src/locales/ru.json | 9 ++++----- src/locales/sco.json | 1 - src/locales/sk.json | 1 - src/locales/sl.json | 9 ++++----- src/locales/sq.json | 24 ++++++++++++------------ src/locales/sv.json | 3 +-- src/locales/te.json | 1 - src/locales/tr.json | 3 +-- src/locales/uk.json | 1 - src/locales/vi.json | 1 - src/locales/zh-hans.json | 3 +-- src/locales/zh-hant.json | 3 +-- 67 files changed, 158 insertions(+), 197 deletions(-) diff --git a/src/locales/af.json b/src/locales/af.json index 9bf302e0..eb04e479 100644 --- a/src/locales/af.json +++ b/src/locales/af.json @@ -25,7 +25,6 @@ "pad.settings.fontType.monospaced": "Monospasie", "pad.importExport.exporthtml": "HTML", "pad.importExport.exportpdf": "PDF", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.modals.userdup.advice": "Maak weer 'n verbinding as u die venster wil gebruik.", "pad.modals.unauth": "Nie toegestaan", "pad.modals.deleted": "Geskrap.", diff --git a/src/locales/ar.json b/src/locales/ar.json index 0fa8c02b..b4ea58dd 100644 --- a/src/locales/ar.json +++ b/src/locales/ar.json @@ -51,7 +51,6 @@ "pad.importExport.exportword": "مايكروسوفت وورد", "pad.importExport.exportpdf": "صيغة المستندات المحمولة", "pad.importExport.exportopen": "ODF (نسق المستند المفتوح)", - "pad.importExport.exportdokuwiki": "دوکوويكي", "pad.importExport.abiword.innerHTML": "لايمكنك الاستيراد إلا من نص عادي أو من تنسيقات إتش تي إم إل. للحصول على المزيد من ميزات الاستيراد المتقدمة، يرجى تثبيت أبيورد .", "pad.modals.connected": "متصل.", "pad.modals.reconnecting": "إعادة الاتصال ببادك", diff --git a/src/locales/ast.json b/src/locales/ast.json index 45ee9391..8eda58c8 100644 --- a/src/locales/ast.json +++ b/src/locales/ast.json @@ -49,7 +49,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "Sólo se pue importar dende los formatos de testu planu o html. Pa carauterístiques d'importación más avanzaes instala abiword.", "pad.modals.connected": "Coneutáu.", "pad.modals.reconnecting": "Reconeutando col to bloc...", diff --git a/src/locales/az.json b/src/locales/az.json index f4504323..25a8ecc2 100644 --- a/src/locales/az.json +++ b/src/locales/az.json @@ -51,7 +51,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Açıq Sənəd Formatı)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "Siz yalnız adi mətndən və ya HTML-dən idxal edə bilərsiniz. İdxalın daha mürəkkəb funksiyaları üçün, zəhmət olmasa, AbiWord-i quraşdırın.", "pad.modals.connected": "Bağlandı.", "pad.modals.reconnecting": "Sizin pad yenidən qoşulur..", diff --git a/src/locales/azb.json b/src/locales/azb.json index b3b76f80..799c5b6b 100644 --- a/src/locales/azb.json +++ b/src/locales/azb.json @@ -2,7 +2,8 @@ "@metadata": { "authors": [ "Amir a57", - "Mousa" + "Mousa", + "Koroğlu" ] }, "index.newPad": "یئنی یادداشت دفترچه سی", @@ -45,7 +46,6 @@ "pad.importExport.exportword": "مایکروسافت وورد", "pad.importExport.exportpdf": "پی دی اف", "pad.importExport.exportopen": "او دی اف", - "pad.importExport.exportdokuwiki": "دوکو ویکی", "pad.modals.connected": "متصل اولدی", "pad.modals.reconnecting": "سیزین یادداشت دفترچه سینه یئنی دن متصیل اولدی", "pad.modals.forcereconnect": "یئنی اتصال اوچون زورلاما", @@ -68,5 +68,21 @@ "timeslider.pageTitle": "{{appTitle}}زمان اسلایدری", "timeslider.toolbar.returnbutton": "یادداشت دفترچه سینه قاییت", "timeslider.toolbar.authors": "یازیچیلار", - "timeslider.toolbar.authorsList": "یازیچی سیز" + "timeslider.toolbar.authorsList": "یازیچی سیز", + "timeslider.month.january": "ژانویه", + "timeslider.month.february": "فوریه", + "timeslider.month.march": "مارس", + "timeslider.month.april": "آپریل", + "timeslider.month.may": "مای", + "timeslider.month.june": "ژوئن", + "timeslider.month.july": "جولای", + "timeslider.month.august": "آقوست", + "timeslider.month.september": "سپتامبر", + "timeslider.month.october": "اوْکتوبر", + "timeslider.month.november": "نوْوامبر", + "timeslider.month.december": "دسامبر", + "pad.userlist.unnamed": "آدسیز", + "pad.userlist.guest": "قوْناق", + "pad.userlist.deny": "دانماق", + "pad.userlist.approve": "اوْنایلا" } diff --git a/src/locales/bcc.json b/src/locales/bcc.json index 10349ae5..7ae42005 100644 --- a/src/locales/bcc.json +++ b/src/locales/bcc.json @@ -49,7 +49,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (قالب سند باز)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "شما تنها می‌توانید از قالب متن ساده یا اچ‌تی‌ام‌ال درون‌ریزی کنید. برای بیشتر شدن ویژگی‌های درون‌ریزی پیشرفته AbiWord را نصب کنید.", "pad.modals.connected": "متصل شد.", "pad.modals.reconnecting": "در حال اتصال دوباره به دفترچه یادداشت شما..", diff --git a/src/locales/be-tarask.json b/src/locales/be-tarask.json index ae29c396..61a78539 100644 --- a/src/locales/be-tarask.json +++ b/src/locales/be-tarask.json @@ -11,14 +11,14 @@ "pad.toolbar.bold.title": "Тоўсты (Ctrl-B)", "pad.toolbar.italic.title": "Курсіў (Ctrl-I)", "pad.toolbar.underline.title": "Падкрэсьліваньне (Ctrl-U)", - "pad.toolbar.strikethrough.title": "Закрэсьліваньне", - "pad.toolbar.ol.title": "Упарадкаваны сьпіс", - "pad.toolbar.ul.title": "Неўпарадкаваны сьпіс", + "pad.toolbar.strikethrough.title": "Закрэсьліваньне (Ctrl+5)", + "pad.toolbar.ol.title": "Упарадкаваны сьпіс (Ctrl+Shift+N)", + "pad.toolbar.ul.title": "Неўпарадкаваны сьпіс (Ctrl+Shift+L)", "pad.toolbar.indent.title": "Водступ (TAB)", "pad.toolbar.unindent.title": "Выступ (Shift+TAB)", "pad.toolbar.undo.title": "Скасаваць(Ctrl-Z)", "pad.toolbar.redo.title": "Вярнуць (Ctrl-Y)", - "pad.toolbar.clearAuthorship.title": "Прыбраць колер дакумэнту", + "pad.toolbar.clearAuthorship.title": "Прыбраць колер дакумэнту (Ctrl+Shift+C)", "pad.toolbar.import_export.title": "Імпарт/Экспарт з выкарыстаньне розных фарматаў файлаў", "pad.toolbar.timeslider.title": "Шкала часу", "pad.toolbar.savedRevision.title": "Захаваць вэрсію", @@ -51,7 +51,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "Вы можаце імпартаваць толькі з звычайнага тэксту або HTML. Дзеля больш пашыраных магчымасьцяў імпарту, калі ласка, усталюйце abiword.", "pad.modals.connected": "Падлучыліся.", "pad.modals.reconnecting": "Перападлучэньне да вашага дакумэнта...", diff --git a/src/locales/bn.json b/src/locales/bn.json index 6c3d6cfc..10a804b4 100644 --- a/src/locales/bn.json +++ b/src/locales/bn.json @@ -4,7 +4,8 @@ "Bellayet", "Nasir8891", "Sankarshan", - "Aftab1995" + "Aftab1995", + "Aftabuzzaman" ] }, "index.newPad": "নতুন প্যাড", @@ -18,7 +19,7 @@ "pad.toolbar.unindent.title": "আউটডেন্ট (Shift+TAB)", "pad.toolbar.undo.title": "বাতিল করুন (Ctrl-Z)", "pad.toolbar.redo.title": "পুনরায় করুন (Ctrl-Y)", - "pad.toolbar.clearAuthorship.title": "কৃতি রং পরিষ্কার করুন", + "pad.toolbar.clearAuthorship.title": "কৃতি রং পরিষ্কার করুন (Ctrl+Shift+C)", "pad.toolbar.timeslider.title": "টাইমস্লাইডার", "pad.toolbar.savedRevision.title": "সংস্করণ সংরক্ষণ করুন", "pad.toolbar.settings.title": "সেটিং", @@ -49,7 +50,6 @@ "pad.importExport.exportword": "মাইক্রোসফট ওয়ার্ড", "pad.importExport.exportpdf": "পিডিএফ", "pad.importExport.exportopen": "ওডিএফ (ওপেন ডকুমেন্ট ফরম্যাট)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.modals.connected": "যোগাযোগ সফল", "pad.modals.reconnecting": "আপনার প্যাডের সাথে সংযোগস্থাপন করা হচ্ছে..", "pad.modals.forcereconnect": "পুনরায় সংযোগস্থাপনের চেষ্টা", diff --git a/src/locales/br.json b/src/locales/br.json index e22802c1..6e455d23 100644 --- a/src/locales/br.json +++ b/src/locales/br.json @@ -12,14 +12,14 @@ "pad.toolbar.bold.title": "Tev (Ctrl-B)", "pad.toolbar.italic.title": "Italek (Ctrl-I)", "pad.toolbar.underline.title": "Islinennañ (Ctrl-U)", - "pad.toolbar.strikethrough.title": "Barrennet", - "pad.toolbar.ol.title": "Roll urzhiet", - "pad.toolbar.ul.title": "Roll en dizurzh", + "pad.toolbar.strikethrough.title": "Barrennet(Ktrl+5)", + "pad.toolbar.ol.title": "Listenn urzhiet (Ktrl+Pennlizherenn+N)", + "pad.toolbar.ul.title": "Listenn en dizurzh (Ktrl+Pennlizherenn+L)", "pad.toolbar.indent.title": "Endantañ (TAB)", "pad.toolbar.unindent.title": "Diendantañ (Shift+TAB)", "pad.toolbar.undo.title": "Dizober (Ktrl-Z)", "pad.toolbar.redo.title": "Adober (Ktrl-Y)", - "pad.toolbar.clearAuthorship.title": "Diverkañ al livioù oc'h anaout an aozerien", + "pad.toolbar.clearAuthorship.title": "Diverkañ al livioù oc'h anaout an aozerien (Ktrl+Pennlizherenn+C)", "pad.toolbar.import_export.title": "Enporzhiañ/Ezporzhiañ eus/war-zu ur furmad restr disheñvel", "pad.toolbar.timeslider.title": "Istor dinamek", "pad.toolbar.savedRevision.title": "Doareoù enrollet", @@ -52,7 +52,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "Ne c'hallit ket emporzjiañ furmadoù testennoù kriz pe html. Evit arc'hwelioù enporzhiañ emdroetoc'h, staliit abiword mar plij.", "pad.modals.connected": "Kevreet.", "pad.modals.reconnecting": "Adkevreañ war-zu ho pad...", diff --git a/src/locales/ca.json b/src/locales/ca.json index f6870db8..92da9d02 100644 --- a/src/locales/ca.json +++ b/src/locales/ca.json @@ -4,7 +4,8 @@ "Alvaro Vidal-Abarca", "Pginer", "Pitort", - "Toniher" + "Toniher", + "Macofe" ] }, "index.newPad": "Nou pad", @@ -19,7 +20,7 @@ "pad.toolbar.unindent.title": "Sagnat invers (Majúsc+TAB)", "pad.toolbar.undo.title": "Desfés (Ctrl-Z)", "pad.toolbar.redo.title": "Refés (Ctrl-Y)", - "pad.toolbar.clearAuthorship.title": "Neteja els colors d'autoria", + "pad.toolbar.clearAuthorship.title": "Neteja els colors d'autoria (Ctrl+Shift+C)", "pad.toolbar.import_export.title": "Importa/exporta a partir de diferents formats de fitxer", "pad.toolbar.timeslider.title": "Línia temporal", "pad.toolbar.savedRevision.title": "Desa la revisió", @@ -52,7 +53,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "Només podeu importar de text net o html. Per a opcions d'importació més avançades instal·leu l'Abiword.", "pad.modals.connected": "Connectat.", "pad.modals.reconnecting": "S'està tornant a connectar al vostre pad…", diff --git a/src/locales/cs.json b/src/locales/cs.json index 62a2302c..1c1357c4 100644 --- a/src/locales/cs.json +++ b/src/locales/cs.json @@ -53,7 +53,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "Importovat můžeš pouze prostý text nebo HTML formátování. Pro pokročilejší funkce importu, prosím, nainstaluj „abiword“.", "pad.modals.connected": "Připojeno.", "pad.modals.reconnecting": "Znovupřipojování k Padu…", diff --git a/src/locales/da.json b/src/locales/da.json index a6a07d14..662e9afb 100644 --- a/src/locales/da.json +++ b/src/locales/da.json @@ -11,14 +11,14 @@ "pad.toolbar.bold.title": "Fed (Ctrl-B)", "pad.toolbar.italic.title": "Kursiv (Ctrl-I)", "pad.toolbar.underline.title": "Understregning (Ctrl-U)", - "pad.toolbar.strikethrough.title": "Gennemstregning", - "pad.toolbar.ol.title": "Sorteret liste", - "pad.toolbar.ul.title": "Usorteret liste", + "pad.toolbar.strikethrough.title": "Gennemstregning (Ctrl+5)", + "pad.toolbar.ol.title": "Sorteret liste (Ctrl+Shift+N)", + "pad.toolbar.ul.title": "Usorteret liste (Ctrl+Shift+L)", "pad.toolbar.indent.title": "Indrykning (TAB)", "pad.toolbar.unindent.title": "Ryk ud (Shift+TAB)", "pad.toolbar.undo.title": "Fortryd (Ctrl-Z)", "pad.toolbar.redo.title": "Annuller Fortryd (Ctrl-Y)", - "pad.toolbar.clearAuthorship.title": "Fjern farver for forfatterskab", + "pad.toolbar.clearAuthorship.title": "Fjern farver for forfatterskab (Ctrl+Shift+C)", "pad.toolbar.import_export.title": "Import/eksport fra/til forskellige filformater", "pad.toolbar.timeslider.title": "Timeslider", "pad.toolbar.savedRevision.title": "Gem Revision", @@ -51,7 +51,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "Du kan kun importere fra almindelig tekst eller HTML-formater. For mere avancerede importfunktioner, installer venligst abiword.", "pad.modals.connected": "Forbundet.", "pad.modals.reconnecting": "Genopretter forbindelsen til din pad...", diff --git a/src/locales/de.json b/src/locales/de.json index 25594da1..f05d43c3 100644 --- a/src/locales/de.json +++ b/src/locales/de.json @@ -19,7 +19,7 @@ "pad.toolbar.unindent.title": "Ausrücken (Shift+TAB)", "pad.toolbar.undo.title": "Rückgängig (Strg-Z)", "pad.toolbar.redo.title": "Wiederholen (Strg-Y)", - "pad.toolbar.clearAuthorship.title": "Autorenfarben zurücksetzen", + "pad.toolbar.clearAuthorship.title": "Autorenfarben zurücksetzen (Strg+Shift+C)", "pad.toolbar.import_export.title": "Import/Export von/zu verschiedenen Dateiformaten", "pad.toolbar.timeslider.title": "Pad-Versionsgeschichte anzeigen", "pad.toolbar.savedRevision.title": "Version markieren", @@ -52,7 +52,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "Sie können nur aus Klartext oder HTML-Formaten importieren. Für mehr erweiterte Importfunktionen installieren Sie bitte abiword.", "pad.modals.connected": "Verbunden.", "pad.modals.reconnecting": "Wiederherstellen der Verbindung …", diff --git a/src/locales/diq.json b/src/locales/diq.json index a98b3c14..61d40169 100644 --- a/src/locales/diq.json +++ b/src/locales/diq.json @@ -10,14 +10,14 @@ "pad.toolbar.bold.title": "Qalın (Ctrl-B)", "pad.toolbar.italic.title": "Namıte (Ctrl-I)", "pad.toolbar.underline.title": "Bınxetın (Ctrl-U)", - "pad.toolbar.strikethrough.title": "Serxetın", - "pad.toolbar.ol.title": "Lista rêzkerdiye", - "pad.toolbar.ul.title": "Lista rêznêkerdiye", + "pad.toolbar.strikethrough.title": "Serxetın (Ctrl+5)", + "pad.toolbar.ol.title": "Lista rêzkerdiye (Ctrl+Shift+N)", + "pad.toolbar.ul.title": "Lista rêznêkerdiye (Ctrl+Shift+L)", "pad.toolbar.indent.title": "Serrêze (TAB)", "pad.toolbar.unindent.title": "Teberdayış (Shift+TAB)", "pad.toolbar.undo.title": "Meke (Ctrl-Z)", "pad.toolbar.redo.title": "Fına bıke (Ctrl-Y)", - "pad.toolbar.clearAuthorship.title": "Rengê Nuştoğiê Arıstey", + "pad.toolbar.clearAuthorship.title": "Rengê Nuştoğiê Arıstey (Ctrl+Shift+C)", "pad.toolbar.timeslider.title": "Ğızagê zemani", "pad.toolbar.savedRevision.title": "Çımraviyarnayışi qeyd ke", "pad.toolbar.settings.title": "Sazkerdışi", @@ -43,7 +43,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.modals.connected": "Gırediya.", "pad.modals.forcereconnect": "Mecbur anciya gırê de", "pad.modals.userdup": "Zewbina pençere de bi a", @@ -80,7 +79,7 @@ "timeslider.month.november": "Tışrino Peyên", "timeslider.month.december": "Kanun", "timeslider.unnamedauthors": "{{num}} unnamed {[plural(num) zu: nuştoğ, zewbi: nustoği ]}", - "pad.userlist.entername": "Namey ğo cı kewe", + "pad.userlist.entername": "Nameyê xo cıkewe", "pad.userlist.unnamed": "Name nébıyo", "pad.userlist.guest": "Meyman", "pad.userlist.deny": "Red ke", diff --git a/src/locales/dsb.json b/src/locales/dsb.json index a24536d4..0600be90 100644 --- a/src/locales/dsb.json +++ b/src/locales/dsb.json @@ -49,7 +49,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "Móžoš jano z fprmatow lutnego teksta abo z HTML-formata importěrowaś. Za wěcej rozšyrjone importěrowańske funkcije instalěruj pšosym Abiword.", "pad.modals.connected": "Zwězany.", "pad.modals.reconnecting": "Zwězujo se znowego z twójim zapisnikom...", diff --git a/src/locales/el.json b/src/locales/el.json index 0c334c1c..f20e1e87 100644 --- a/src/locales/el.json +++ b/src/locales/el.json @@ -13,14 +13,14 @@ "pad.toolbar.bold.title": "Έντονη (Ctrl-B)", "pad.toolbar.italic.title": "Πλάγια (Ctrl-I)", "pad.toolbar.underline.title": "Υπογράμμιση (Ctrl-U)", - "pad.toolbar.strikethrough.title": "Διακριτή διαγραφή", - "pad.toolbar.ol.title": "Ταξινομημένη λίστα", - "pad.toolbar.ul.title": "Λίστα χωρίς σειρά", + "pad.toolbar.strikethrough.title": "Διακριτή διαγραφή (Ctrl+5)", + "pad.toolbar.ol.title": "Ταξινομημένη λίστα (Ctrl+Shift+N)", + "pad.toolbar.ul.title": "Λίστα χωρίς σειρά (Ctrl+Shift+L)", "pad.toolbar.indent.title": "Αριστερά εσοχή (TAB)", "pad.toolbar.unindent.title": "Δεξιά εσοχή (Shift+TAB)", "pad.toolbar.undo.title": "Αναίρεση (Ctrl-Z)", "pad.toolbar.redo.title": "Επανάληψη (Ctrl-Y)", - "pad.toolbar.clearAuthorship.title": "Εκκαθάριση των χρωμάτων των συντακτών", + "pad.toolbar.clearAuthorship.title": "Εκκαθάριση των χρωμάτων των συντακτών (Ctrl+Shift+C)", "pad.toolbar.import_export.title": "Εισαγωγή/Εξαγωγή από/σε διαφορετικούς τύπους αρχείων", "pad.toolbar.timeslider.title": "Χρονοδιάγραμμα", "pad.toolbar.savedRevision.title": "Αποθήκευση Αναθεώρησης", @@ -53,14 +53,13 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Μορφή Open Document)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "Μπορείτε να κάνετε εισαγωγή απλού κειμένου ή μορφής html. Για πιο προηγμένες δυνατότητες εισαγωγής παρακαλώ εγκαταστήστε το abiword.", "pad.modals.connected": "Συνδεμένοι.", "pad.modals.reconnecting": "Επανασύνδεση στο pad σας...", "pad.modals.forcereconnect": "Επιβολή επανασύνδεσης", "pad.modals.userdup": "Ανοιγμένο σε άλλο παράθυρο", "pad.modals.userdup.explanation": "Αυτό το pad φαίνεται να είναι ανοιχτό σε περισσότερα από ένα παράθυρο του προγράμματος περιήγησης σε αυτόν τον υπολογιστή.", - "pad.modals.userdup.advice": "Επανασύνδεση για να χρησιμοποιήσετε αυτό το παράθυρο.", + "pad.modals.userdup.advice": "Επανασυνδεθείτε για να χρησιμοποιήσετε αυτό το παράθυρο.", "pad.modals.unauth": "Δεν επιτρέπεται", "pad.modals.unauth.explanation": "Τα δικαιώματά σας άλλαξαν όσο βλέπατε αυτήν τη σελίδα. Δοκιμάστε να επανασυνδεθείτε.", "pad.modals.looping.explanation": "Υπάρχουν προβλήματα επικοινωνίας με τον διακομιστή συγχρονισμού.", @@ -76,7 +75,7 @@ "pad.modals.corruptPad.cause": "Αυτό μπορεί να οφείλεται σε ένα λάθος στη ρύθμιση του διακομιστή ή κάποια άλλη απρόβλεπτη συμπεριφορά. Παρακαλώ επικοινωνήστε με τον διαχειριστή της υπηρεσίας.", "pad.modals.deleted": "Διεγράφη.", "pad.modals.deleted.explanation": "Αυτό το pad έχει καταργηθεί.", - "pad.modals.disconnected": "Έχετε αποσυνδεθεί.", + "pad.modals.disconnected": "Είστε αποσυνδεδεμένοι.", "pad.modals.disconnected.explanation": "Χάθηκε η σύνδεση με τον διακομιστή", "pad.modals.disconnected.cause": "Ο διακομιστής μπορεί να μην είναι διαθέσιμος. Παρακαλούμε ειδοποιήστε τον διαχειριστή της υπηρεσίας εάν εξακολουθεί να συμβαίνει αυτό.", "pad.share": "Μοιραστείτε αυτό το pad", diff --git a/src/locales/es.json b/src/locales/es.json index 62e255ea..df9a415e 100644 --- a/src/locales/es.json +++ b/src/locales/es.json @@ -25,7 +25,7 @@ "pad.toolbar.unindent.title": "Eliminar sangría (Shift+TAB)", "pad.toolbar.undo.title": "Deshacer (Ctrl-Z)", "pad.toolbar.redo.title": "Rehacer (Ctrl-Y)", - "pad.toolbar.clearAuthorship.title": "Eliminar los colores de autoría", + "pad.toolbar.clearAuthorship.title": "Eliminar los colores de autoría (Ctrl+Shift+C)", "pad.toolbar.import_export.title": "Importar/Exportar a diferentes formatos de archivos", "pad.toolbar.timeslider.title": "Línea de tiempo", "pad.toolbar.savedRevision.title": "Guardar revisión", @@ -58,7 +58,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "Sólo puedes importar formatos de texto plano o html. Para funciones más avanzadas instala abiword.", "pad.modals.connected": "Conectado.", "pad.modals.reconnecting": "Reconectando a tu pad..", @@ -100,18 +99,18 @@ "timeslider.version": "Versión {{version}}", "timeslider.saved": "Guardado el {{day}} de {{month}} de {{year}}", "timeslider.dateformat": "{{day}}/{{month}}/{{year}} {{hours}}:{{minutes}}:{{seconds}}", - "timeslider.month.january": "Enero", - "timeslider.month.february": "Febrero", - "timeslider.month.march": "Marzo", - "timeslider.month.april": "Abril", - "timeslider.month.may": "Mayo", - "timeslider.month.june": "Junio", - "timeslider.month.july": "Julio", - "timeslider.month.august": "Agosto", - "timeslider.month.september": "Septiembre", - "timeslider.month.october": "Octubre", - "timeslider.month.november": "Noviembre", - "timeslider.month.december": "Diciembre", + "timeslider.month.january": "enero", + "timeslider.month.february": "febrero", + "timeslider.month.march": "marzo", + "timeslider.month.april": "abril", + "timeslider.month.may": "mayo", + "timeslider.month.june": "junio", + "timeslider.month.july": "julio", + "timeslider.month.august": "agosto", + "timeslider.month.september": "septiembre", + "timeslider.month.october": "octubre", + "timeslider.month.november": "noviembre", + "timeslider.month.december": "diciembre", "timeslider.unnamedauthors": "{{num}} {[ plural(num) one: autor desconocido, other: autores desconocidos]}", "pad.savedrevs.marked": "Revisión guardada", "pad.userlist.entername": "Escribe tu nombre", diff --git a/src/locales/et.json b/src/locales/et.json index 29cd92b9..3ea8b3e6 100644 --- a/src/locales/et.json +++ b/src/locales/et.json @@ -49,7 +49,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "Paraku on ainult lihttekstis voi HTML-vormingus dokumentide importimine võimaldatud. Rohkem võimaluste jaoks peab paigaldama abiword.", "pad.modals.connected": "Ühendatud.", "pad.modals.reconnecting": "Proovitakse luua ühendus klade juurde...", diff --git a/src/locales/eu.json b/src/locales/eu.json index 5aa3ec4e..f12b8d21 100644 --- a/src/locales/eu.json +++ b/src/locales/eu.json @@ -49,7 +49,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DocuWiki", "pad.importExport.abiword.innerHTML": "Testu laua edo html formatudun testuak bakarrik inporta ditzakezu. Aurreratuagoak diren inportazio aukerak izateko abiword instala ezazu.", "pad.modals.connected": "Konektatuta.", "pad.modals.reconnecting": "Zure pad-era birkonektatu...", diff --git a/src/locales/fa.json b/src/locales/fa.json index 3b421972..94f2d69f 100644 --- a/src/locales/fa.json +++ b/src/locales/fa.json @@ -14,14 +14,14 @@ "pad.toolbar.bold.title": "پررنگ (Ctrl-B)", "pad.toolbar.italic.title": "کج (Ctrl-I)", "pad.toolbar.underline.title": "زیرخط (Ctrl-U)", - "pad.toolbar.strikethrough.title": "خط خورده", - "pad.toolbar.ol.title": "فهرست مرتب شده", - "pad.toolbar.ul.title": "فهرست مرتب نشده", + "pad.toolbar.strikethrough.title": "خط خورده (Ctrl+5)", + "pad.toolbar.ol.title": "فهرست مرتب شده (Ctrl+Shift+N)", + "pad.toolbar.ul.title": "فهرست مرتب نشده (Ctrl+Shift+L)", "pad.toolbar.indent.title": "تورفتگی (TAB)", "pad.toolbar.unindent.title": "بیرون رفتگی (Shift+TAB)", "pad.toolbar.undo.title": "باطل‌کردن (Ctrl-Z)", "pad.toolbar.redo.title": "از نو (Ctrl-Y)", - "pad.toolbar.clearAuthorship.title": "پاک‌کردن رنگ‌های نویسندگی", + "pad.toolbar.clearAuthorship.title": "پاک‌کردن رنگ‌های نویسندگی (Ctrl+Shift+C)", "pad.toolbar.import_export.title": "درون‌ریزی/برون‌ریزی از/به قالب‌های مختلف", "pad.toolbar.timeslider.title": "لغزندهٔ زمان", "pad.toolbar.savedRevision.title": "ذخیره‌سازی نسخه", @@ -54,7 +54,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (قالب سند باز)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "شما تنها می‌توانید از قالب متن ساده یا اچ‌تی‌ام‌ال درون‌ریزی کنید. برای بیشتر شدن ویژگی‌های درون‌ریزی پیشرفته AbiWord را نصب کنید.", "pad.modals.connected": "متصل شد.", "pad.modals.reconnecting": "در حال اتصال دوباره به دفترچه یادداشت شما..", diff --git a/src/locales/fi.json b/src/locales/fi.json index 656f0a21..ee1775dc 100644 --- a/src/locales/fi.json +++ b/src/locales/fi.json @@ -9,7 +9,8 @@ "Stryn", "Tomi Toivio", "Veikk0.ma", - "VezonThunder" + "VezonThunder", + "Macofe" ] }, "index.newPad": "Uusi muistio", @@ -17,14 +18,14 @@ "pad.toolbar.bold.title": "Lihavointi (Ctrl-B)", "pad.toolbar.italic.title": "Kursivointi (Ctrl-I)", "pad.toolbar.underline.title": "Alleviivaus (Ctrl-U)", - "pad.toolbar.strikethrough.title": "Yliviivaus", - "pad.toolbar.ol.title": "Numeroitu lista", - "pad.toolbar.ul.title": "Numeroimaton lista", + "pad.toolbar.strikethrough.title": "Yliviivaus (Ctrl+5)", + "pad.toolbar.ol.title": "Numeroitu lista (Ctrl+Shift+N)", + "pad.toolbar.ul.title": "Numeroimaton lista (Ctrl+Shift+L)", "pad.toolbar.indent.title": "Sisennä (TAB)", "pad.toolbar.unindent.title": "Ulonna (Shift+TAB)", "pad.toolbar.undo.title": "Kumoa (Ctrl-Z)", "pad.toolbar.redo.title": "Tee uudelleen (Ctrl-Y)", - "pad.toolbar.clearAuthorship.title": "Poista kirjoittajavärit", + "pad.toolbar.clearAuthorship.title": "Poista kirjoittajavärit (Ctrl+Shift+C)", "pad.toolbar.import_export.title": "Tuo tai vie eri tiedostomuodoista tai -muotoihin", "pad.toolbar.timeslider.title": "Aikajana", "pad.toolbar.savedRevision.title": "Tallenna muutos", @@ -57,7 +58,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "Tuonti on tuettu vain HTML- ja raakatekstitiedostoista. Lisätietoja tuonnin lisäasetuksista on sivulla install abiword.", "pad.modals.connected": "Yhdistetty.", "pad.modals.reconnecting": "Muodostetaan yhteyttä muistioon uudelleen...", diff --git a/src/locales/fo.json b/src/locales/fo.json index cd72dec7..43e9d9f1 100644 --- a/src/locales/fo.json +++ b/src/locales/fo.json @@ -43,7 +43,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Opið Dokument Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "Tú kanst bert innflyta frá einføldum teksti ella html formatum. Fyri funksjónir til innflytan fyri víðarikomin vinarliga installera abiword.", "pad.modals.connected": "Tú hevur samband.", "pad.modals.reconnecting": "Roynir aftur at fáa samband við tín pad..", diff --git a/src/locales/fr.json b/src/locales/fr.json index b6b1a720..921d1eeb 100644 --- a/src/locales/fr.json +++ b/src/locales/fr.json @@ -17,7 +17,8 @@ "Stephane Cottin", "Tux-tn", "Maxim21", - "Boniface" + "Boniface", + "Macofe" ] }, "index.newPad": "Nouveau pad", @@ -32,7 +33,7 @@ "pad.toolbar.unindent.title": "Désindenter (Maj+TAB)", "pad.toolbar.undo.title": "Annuler (Ctrl-Z)", "pad.toolbar.redo.title": "Rétablir (Ctrl-Y)", - "pad.toolbar.clearAuthorship.title": "Effacer les couleurs identifiant les auteurs", + "pad.toolbar.clearAuthorship.title": "Effacer les couleurs identifiant les auteurs (Ctrl+Shift+C)", "pad.toolbar.import_export.title": "Importer/Exporter de/vers un format de fichier différent", "pad.toolbar.timeslider.title": "Historique dynamique", "pad.toolbar.savedRevision.title": "Enregistrer la révision", @@ -65,7 +66,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "Vous ne pouvez importer que des formats texte brut ou html. Pour des fonctionnalités d'importation plus évoluées, veuillez installer abiword.", "pad.modals.connected": "Connecté.", "pad.modals.reconnecting": "Reconnexion vers votre pad...", diff --git a/src/locales/gl.json b/src/locales/gl.json index 93855a8b..a14fbb12 100644 --- a/src/locales/gl.json +++ b/src/locales/gl.json @@ -1,7 +1,8 @@ { "@metadata": { "authors": [ - "Toliño" + "Toliño", + "Elisardojm" ] }, "index.newPad": "Novo documento", @@ -9,14 +10,14 @@ "pad.toolbar.bold.title": "Negra (Ctrl-B)", "pad.toolbar.italic.title": "Cursiva (Ctrl-I)", "pad.toolbar.underline.title": "Subliñar (Ctrl-U)", - "pad.toolbar.strikethrough.title": "Riscar", - "pad.toolbar.ol.title": "Lista ordenada", - "pad.toolbar.ul.title": "Lista sen ordenar", + "pad.toolbar.strikethrough.title": "Riscar (Ctrl+5)", + "pad.toolbar.ol.title": "Lista ordenada (Ctrl+Shift+N)", + "pad.toolbar.ul.title": "Lista sen ordenar (Ctrl+Shift+L)", "pad.toolbar.indent.title": "Sangría (TAB)", "pad.toolbar.unindent.title": "Sen sangría (Maiús.+TAB)", "pad.toolbar.undo.title": "Desfacer (Ctrl-Z)", "pad.toolbar.redo.title": "Refacer (Ctrl-Y)", - "pad.toolbar.clearAuthorship.title": "Limpar as cores de identificación dos autores", + "pad.toolbar.clearAuthorship.title": "Limpar as cores de identificación dos autores (Ctrl+Shift+C)", "pad.toolbar.import_export.title": "Importar/Exportar desde/a diferentes formatos de ficheiro", "pad.toolbar.timeslider.title": "Liña do tempo", "pad.toolbar.savedRevision.title": "Gardar a revisión", @@ -49,7 +50,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "Só pode importar texto simple ou formatos HTML. Para obter máis información sobre as características de importación avanzadas instale abiword.", "pad.modals.connected": "Conectado.", "pad.modals.reconnecting": "Reconectando co seu documento...", diff --git a/src/locales/he.json b/src/locales/he.json index 7d7f2b78..573bc5f6 100644 --- a/src/locales/he.json +++ b/src/locales/he.json @@ -19,7 +19,7 @@ "pad.toolbar.unindent.title": "צמצום הזחה (שיפט–טאב)", "pad.toolbar.undo.title": "ביטול (Ctrl-Z)", "pad.toolbar.redo.title": "ביצוע מחדש", - "pad.toolbar.clearAuthorship.title": "ניקוי צבעים", + "pad.toolbar.clearAuthorship.title": "ניקוי צבעי כותבים (Ctrl-Shift-C)", "pad.toolbar.import_export.title": "ייבוא/ייצוא בתסדירי קבצים שונים", "pad.toolbar.timeslider.title": "גולל זמן", "pad.toolbar.savedRevision.title": "שמירת גרסה", @@ -52,7 +52,6 @@ "pad.importExport.exportword": "מיקרוסופט וורד", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "באפשרותך לייבא מטקסט פשוט או מ־HTML. לאפשרויות ייבוא מתקדמות יותר יש להתקין את abiword.", "pad.modals.connected": "מחובר.", "pad.modals.reconnecting": "מתבצע חיבור מחדש...", diff --git a/src/locales/hrx.json b/src/locales/hrx.json index 808a1297..74630efa 100644 --- a/src/locales/hrx.json +++ b/src/locales/hrx.json @@ -49,7 +49,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "Sie können nur aus Klartext oder HTML-Formaten importieren. Für mehr erweiterte Importfunktionen installieren Sie bitte abiword.", "pad.modals.connected": "Verbünd (konnektiert).", "pad.modals.reconnecting": "Wiederherstelle von der Verbinnung …", diff --git a/src/locales/hsb.json b/src/locales/hsb.json index c86f6078..ddf3cc4a 100644 --- a/src/locales/hsb.json +++ b/src/locales/hsb.json @@ -49,7 +49,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "Móžeš jenož z formatow luteho teksta abo z HTML-formata importować. Za bóle rozšěrjene importowanske funkcije instaluj prošu Abiword.", "pad.modals.connected": "Zwjazany.", "pad.modals.reconnecting": "Zwjazuje so znowa z twojim zapisnikom...", diff --git a/src/locales/hu.json b/src/locales/hu.json index 2cbf5749..7efac2df 100644 --- a/src/locales/hu.json +++ b/src/locales/hu.json @@ -13,14 +13,14 @@ "pad.toolbar.bold.title": "Félkövér (Ctrl-B)", "pad.toolbar.italic.title": "Dőlt (Ctrl-I)", "pad.toolbar.underline.title": "Aláhúzás (Ctrl-U)", - "pad.toolbar.strikethrough.title": "Áthúzás", - "pad.toolbar.ol.title": "Számozott lista", - "pad.toolbar.ul.title": "Számozatlan lista", + "pad.toolbar.strikethrough.title": "Áthúzás (Ctrl+5)", + "pad.toolbar.ol.title": "Számozott lista (Ctrl+Shift+N)", + "pad.toolbar.ul.title": "Számozatlan lista (Ctrl+Shift+L)", "pad.toolbar.indent.title": "Behúzás növelése (TAB)", "pad.toolbar.unindent.title": "Behúzás csökkentése (Shift+TAB)", "pad.toolbar.undo.title": "Visszavonás (Ctrl-Z)", "pad.toolbar.redo.title": "Újra (Ctrl-Y)", - "pad.toolbar.clearAuthorship.title": "Szerzők színezésének kikapcsolása", + "pad.toolbar.clearAuthorship.title": "Szerzők színezésének kikapcsolása (Ctrl+Shift+C)", "pad.toolbar.import_export.title": "Importálás/exportálás különböző fájlformátumokból/ba", "pad.toolbar.timeslider.title": "Időcsúszka", "pad.toolbar.savedRevision.title": "Revízió mentése", @@ -53,7 +53,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document formátum)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "Csak szöveges, vagy HTML formátumokból importálhatsz. A speciális importálási funkciókért kérjük telepítsd az abiword-öt.", "pad.modals.connected": "Kapcsolódva.", "pad.modals.reconnecting": "Újrakapcsolódás a noteszhez...", diff --git a/src/locales/ia.json b/src/locales/ia.json index 971f2919..50a0690c 100644 --- a/src/locales/ia.json +++ b/src/locales/ia.json @@ -9,14 +9,14 @@ "pad.toolbar.bold.title": "Grasse (Ctrl-B)", "pad.toolbar.italic.title": "Italic (Ctrl-I)", "pad.toolbar.underline.title": "Sublinear (Ctrl-U)", - "pad.toolbar.strikethrough.title": "Cancellar", - "pad.toolbar.ol.title": "Lista ordinate", - "pad.toolbar.ul.title": "Lista non ordinate", + "pad.toolbar.strikethrough.title": "Cancellar (Ctrl+5)", + "pad.toolbar.ol.title": "Lista ordinate (Ctrl+Shift+N)", + "pad.toolbar.ul.title": "Lista non ordinate (Ctrl+Shift+L)", "pad.toolbar.indent.title": "Indentar (TAB)", "pad.toolbar.unindent.title": "Disindentar (Shift+TAB)", "pad.toolbar.undo.title": "Disfacer (Ctrl-Z)", "pad.toolbar.redo.title": "Refacer (Ctrl-Y)", - "pad.toolbar.clearAuthorship.title": "Rader colores de autor", + "pad.toolbar.clearAuthorship.title": "Rader colores de autor (Ctrl+Shift+C)", "pad.toolbar.import_export.title": "Importar/exportar in differente formatos de file", "pad.toolbar.timeslider.title": "Glissa-tempore", "pad.toolbar.savedRevision.title": "Version salveguardate", @@ -49,7 +49,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "Tu pote solmente importar files in formato de texto simple o HTML. Pro functionalitate de importation plus extense, installa AbiWord.", "pad.modals.connected": "Connectite.", "pad.modals.reconnecting": "Reconnecte a tu pad…", diff --git a/src/locales/it.json b/src/locales/it.json index 501733f8..02ae8a9f 100644 --- a/src/locales/it.json +++ b/src/locales/it.json @@ -20,7 +20,7 @@ "pad.toolbar.unindent.title": "Riduci rientro (Shift+TAB)", "pad.toolbar.undo.title": "Annulla (Ctrl-Z)", "pad.toolbar.redo.title": "Ripeti (Ctrl-Y)", - "pad.toolbar.clearAuthorship.title": "Elimina i colori che indicano gli autori", + "pad.toolbar.clearAuthorship.title": "Elimina i colori che indicano gli autori (Ctrl+Shift+C)", "pad.toolbar.import_export.title": "Importa/esporta da/a diversi formati di file", "pad.toolbar.timeslider.title": "Presentazione cronologia", "pad.toolbar.savedRevision.title": "Versione salvata", @@ -53,7 +53,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "È possibile importare solo i formati di testo semplice o HTML. Per metodi più avanzati di importazione installare Abiword.", "pad.modals.connected": "Connesso.", "pad.modals.reconnecting": "Riconnessione al pad in corso...", diff --git a/src/locales/ja.json b/src/locales/ja.json index 011c935a..62e6dc62 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -49,7 +49,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "プレーンテキストまたは HTML ファイルからのみインポートできます。より高度なインポート機能を使用するには、abiword をインストールしてください。", "pad.modals.connected": "接続されました。", "pad.modals.reconnecting": "パッドに再接続中...", diff --git a/src/locales/km.json b/src/locales/km.json index a90c0e97..4dea037a 100644 --- a/src/locales/km.json +++ b/src/locales/km.json @@ -47,7 +47,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.modals.connected": "បាន​តភ្ជាប់​។", "pad.modals.reconnecting": "កំពុង​ភ្ជាប់​ទៅ​ផេត​របស់​អ្នក​ម្ដង​ទៀត..", "pad.modals.forcereconnect": "បង្ខំ​ឲ្យ​ភ្ជាប់​ឡើង​វិញ", diff --git a/src/locales/ko.json b/src/locales/ko.json index 1fed980f..e18c5bbd 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -11,14 +11,14 @@ "pad.toolbar.bold.title": "굵게 (Ctrl-B)", "pad.toolbar.italic.title": "기울임 (Ctrl-I)", "pad.toolbar.underline.title": "밑줄 (Ctrl-U)", - "pad.toolbar.strikethrough.title": "취소선", - "pad.toolbar.ol.title": "순서 있는 목록", - "pad.toolbar.ul.title": "순서 없는 목록", + "pad.toolbar.strikethrough.title": "취소선 (Ctrl+5)", + "pad.toolbar.ol.title": "순서 있는 목록 (Ctrl+Shift+N)", + "pad.toolbar.ul.title": "순서 없는 목록 (Ctrl+Shift+L)", "pad.toolbar.indent.title": "들여쓰기 (TAB)", "pad.toolbar.unindent.title": "내어쓰기 (Shift+TAB)", "pad.toolbar.undo.title": "실행 취소 (Ctrl-Z)", "pad.toolbar.redo.title": "다시 실행 (Ctrl-Y)", - "pad.toolbar.clearAuthorship.title": "저자의 색 지우기", + "pad.toolbar.clearAuthorship.title": "저자의 색 지우기 (Ctrl+Shift+C)", "pad.toolbar.import_export.title": "다른 파일 형식으로 가져오기/내보내기", "pad.toolbar.timeslider.title": "시간슬라이더", "pad.toolbar.savedRevision.title": "판 저장", @@ -51,7 +51,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "일반 텍스트나 html 형식으로만 가져올 수 있습니다. 고급 가져오기 기능에 대해서는 abiword를 설치하세요.", "pad.modals.connected": "연결했습니다.", "pad.modals.reconnecting": "패드에 다시 연결 중..", diff --git a/src/locales/ksh.json b/src/locales/ksh.json index 1de3c450..8443e0ec 100644 --- a/src/locales/ksh.json +++ b/src/locales/ksh.json @@ -9,14 +9,14 @@ "pad.toolbar.bold.title": "Fättschreff (Strg-B)", "pad.toolbar.italic.title": "Scheive Schreff (Strg-I)", "pad.toolbar.underline.title": "Ongerstresche (Strg-U)", - "pad.toolbar.strikethrough.title": "Dorschjeschtresche", - "pad.toolbar.ol.title": "Leß met Nommere", - "pad.toolbar.ul.title": "Leß met Pongkte", + "pad.toolbar.strikethrough.title": "Dorschjeschtresche (Strg+5)", + "pad.toolbar.ol.title": "Leß met Nommere (Strg+Jruhß+N)", + "pad.toolbar.ul.title": "Leß met Pongkte (Strg+Jruhß+L)", "pad.toolbar.indent.title": "Enjerök (TAB)", "pad.toolbar.unindent.title": "Ußjerök (Jruhßschreff+TAB)", "pad.toolbar.undo.title": "Retuur nämme (Strg-Z)", "pad.toolbar.redo.title": "Norrens (Strg-Y)", - "pad.toolbar.clearAuthorship.title": "Donn dä Schriiver ier Färve fottnämme", + "pad.toolbar.clearAuthorship.title": "Donn dä Schriiver ier Färve fottnämme (Strg+Jruhß+C)", "pad.toolbar.import_export.title": "Ongerscheidlijje Dattei_Fommaate empotteere udder äxpotteere", "pad.toolbar.timeslider.title": "Verjangeheid afschpelle", "pad.toolbar.savedRevision.title": "Di Väsjohn faßhallde", @@ -49,7 +49,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF (Poteerbaa Dokemänte Fommaat)", "pad.importExport.exportopen": "ODF (Offe Dokemänte-Fommaat)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "Mer künne bloß eijfaache Täxte udder HTML_Fommaate empoteere. Opwändejere Müjjeleschkeite fö der Empoot jon och, doför bruch mer en Enschtallazjuhn met Abiword.", "pad.modals.connected": "Verbonge.", "pad.modals.reconnecting": "Ben wider aam Verbenge …", diff --git a/src/locales/lrc.json b/src/locales/lrc.json index 322b6f23..910f8f1e 100644 --- a/src/locales/lrc.json +++ b/src/locales/lrc.json @@ -36,7 +36,6 @@ "pad.importExport.exportword": "واجه پالایشتگر مایکروسافت", "pad.importExport.exportpdf": "پی دی اف", "pad.importExport.exportopen": "او دی اف(قالو سند وا بیه)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.modals.connected": "وصل بیه", "pad.modals.forcereconnect": "سی وصل بین مژبور کو", "pad.modals.userdup": "د نیمدری هنی واز بیه", diff --git a/src/locales/lt.json b/src/locales/lt.json index 503b6589..7b4e1481 100644 --- a/src/locales/lt.json +++ b/src/locales/lt.json @@ -28,7 +28,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Atvirasis dokumento formatas)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.modals.connected": "Prisijungta.", "pad.modals.unauth": "Neleidžiama", "pad.modals.initsocketfail": "Serveris yra nepasiekiamas.", diff --git a/src/locales/lv.json b/src/locales/lv.json index e57c4259..acdaaccd 100644 --- a/src/locales/lv.json +++ b/src/locales/lv.json @@ -46,7 +46,6 @@ "pad.importExport.exportword": "Programma Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open dokumenta formāts)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.modals.userdup": "Atvērts citā logā", "pad.modals.unauth": "Nav atļauts", "pad.modals.looping.explanation": "Pastāv sakaru problēmas ar sinhronizācijas servera.", diff --git a/src/locales/map-bms.json b/src/locales/map-bms.json index 883b1d8f..1b87bac8 100644 --- a/src/locales/map-bms.json +++ b/src/locales/map-bms.json @@ -49,7 +49,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "Rika mung teyeng impor sekang format plain text utawa HTML. Kanggo fitur impor sing lewih maju monggo masang abiword.", "pad.modals.connected": "Nyambung.", "pad.modals.reconnecting": "Mbaleli nyambung ming pad Rika...", diff --git a/src/locales/mk.json b/src/locales/mk.json index aec40b27..fc18533f 100644 --- a/src/locales/mk.json +++ b/src/locales/mk.json @@ -17,7 +17,7 @@ "pad.toolbar.unindent.title": "Отстап (Shift+TAB)", "pad.toolbar.undo.title": "Врати (Ctrl-Z)", "pad.toolbar.redo.title": "Повтори (Ctrl-Y)", - "pad.toolbar.clearAuthorship.title": "Поништи ги авторските бои", + "pad.toolbar.clearAuthorship.title": "Тргни ги авторските бои (Ctrl+Shift+C)", "pad.toolbar.import_export.title": "Увоз/Извоз од/во разни податотечни формати", "pad.toolbar.timeslider.title": "Историски преглед", "pad.toolbar.savedRevision.title": "Зачувај преработка", @@ -50,7 +50,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "Можете да увезувате само од прост текст и HTML-формат. Понапредни можности за увоз ќе добиете ако воспоставите AbiWord.", "pad.modals.connected": "Поврзано.", "pad.modals.reconnecting": "Ве преповрзувам со тетратката...", diff --git a/src/locales/ml.json b/src/locales/ml.json index ff7a38f3..680df0c2 100644 --- a/src/locales/ml.json +++ b/src/locales/ml.json @@ -13,14 +13,14 @@ "pad.toolbar.bold.title": "കടുപ്പത്തിലെഴുതുക (Ctrl-B)", "pad.toolbar.italic.title": "ചെരിച്ചെഴുതുക (Ctrl-I)", "pad.toolbar.underline.title": "അടിവരയിടുക (Ctrl-U)", - "pad.toolbar.strikethrough.title": "വെട്ടുക", - "pad.toolbar.ol.title": "ക്രമത്തിലുള്ള പട്ടിക", - "pad.toolbar.ul.title": "ക്രമരഹിത പട്ടിക", + "pad.toolbar.strikethrough.title": "വെട്ടുക (Ctrl+5)", + "pad.toolbar.ol.title": "ക്രമത്തിലുള്ള പട്ടിക (Ctrl+Shift+N)", + "pad.toolbar.ul.title": "ക്രമരഹിത പട്ടിക (Ctrl+Shift+L)", "pad.toolbar.indent.title": "വലത്തേക്ക് തള്ളുക (ടാബ്)", "pad.toolbar.unindent.title": "ഇടത്തേക്ക് തള്ളുക (ഷിഫ്റ്റ്+ടാബ്)", "pad.toolbar.undo.title": "തിരസ്കരിക്കുക (Ctrl-Z)", "pad.toolbar.redo.title": "വീണ്ടും ചെയ്യുക (Ctrl-Y)", - "pad.toolbar.clearAuthorship.title": "രചയിതാക്കൾക്കുള്ള നിറം കളയുക", + "pad.toolbar.clearAuthorship.title": "രചയിതാക്കൾക്കുള്ള നിറം കളയുക (Ctrl+Shift+C)", "pad.toolbar.import_export.title": "വ്യത്യസ്ത ഫയൽ തരങ്ങളിലേക്ക്/തരങ്ങളിൽ നിന്ന് ഇറക്കുമതി/കയറ്റുമതി ചെയ്യുക", "pad.toolbar.timeslider.title": "സമയരേഖ", "pad.toolbar.savedRevision.title": "നാൾപ്പതിപ്പ് സേവ് ചെയ്യുക", @@ -53,7 +53,6 @@ "pad.importExport.exportword": "മൈക്രോസോഫ്റ്റ് വേഡ്", "pad.importExport.exportpdf": "പി.ഡി.എഫ്.", "pad.importExport.exportopen": "ഒ.ഡി.എഫ്. (ഓപ്പൺ ഡോക്യുമെന്റ് ഫോർമാറ്റ്)", - "pad.importExport.exportdokuwiki": "ഡോകുവിക്കി", "pad.importExport.abiword.innerHTML": "പ്ലെയിൻ ടെക്സ്റ്റോ എച്ച്.റ്റി.എം.എൽ. തരമോ മാത്രമേ താങ്കൾക്ക് ഇറക്കുമതി ചെയ്യാനാവൂ. കൂടുതൽ വിപുലീകൃത ഇറക്കുമതി സൗകര്യങ്ങൾക്കായി ദയവായി അബിവേഡ് ഇൻസ്റ്റോൾ ചെയ്യുക.", "pad.modals.connected": "ബന്ധിപ്പിച്ചിരിക്കുന്നു.", "pad.modals.reconnecting": "താങ്കളുടെ പാഡിലേയ്ക്ക് വീണ്ടും ബന്ധിപ്പിക്കുന്നു...", diff --git a/src/locales/mr.json b/src/locales/mr.json index 31de89aa..536b578c 100644 --- a/src/locales/mr.json +++ b/src/locales/mr.json @@ -26,7 +26,6 @@ "pad.importExport.exportword": "मायक्रोसॉफ्ट वर्ड", "pad.importExport.exportpdf": "पीडीएफ", "pad.importExport.exportopen": "ओडीएफ(ओपन डॉक्यूमेंट फॉरमॅट)", - "pad.importExport.exportdokuwiki": "डुकुविकि", "pad.modals.connected": "अनुबंधित", "pad.modals.initsocketfail": "विदागारास पोच नाही.", "pad.modals.deleted": "वगळले.", diff --git a/src/locales/ms.json b/src/locales/ms.json index 033de1bc..cf15be6e 100644 --- a/src/locales/ms.json +++ b/src/locales/ms.json @@ -9,14 +9,14 @@ "pad.toolbar.bold.title": "Tebal (Ctrl-B)", "pad.toolbar.italic.title": "Miring (Ctrl-I)", "pad.toolbar.underline.title": "Garis bawah (Ctrl-U)", - "pad.toolbar.strikethrough.title": "Garis lorek", - "pad.toolbar.ol.title": "Senarai tertib", - "pad.toolbar.ul.title": "Senarai tak tertib", + "pad.toolbar.strikethrough.title": "Garis lorek (Ctrl+5)", + "pad.toolbar.ol.title": "Senarai tertib (Ctrl+Shift+N)", + "pad.toolbar.ul.title": "Senarai tak tertib (Ctrl+Shift+L)", "pad.toolbar.indent.title": "Engsot ke dalam (TAB)", "pad.toolbar.unindent.title": "Engsot ke luar (Shift + TAB)", "pad.toolbar.undo.title": "Buat asal (Ctrl-Z)", "pad.toolbar.redo.title": "Buat semula (Ctrl-Y)", - "pad.toolbar.clearAuthorship.title": "Padamkan Warna Pengarang", + "pad.toolbar.clearAuthorship.title": "Padamkan Warna Pengarang (Ctrl+Shift+C)", "pad.toolbar.import_export.title": "Import/Eksport dari/ke format-format fail berbeza", "pad.toolbar.timeslider.title": "Gelangsar masa", "pad.toolbar.savedRevision.title": "Simpan Semakan", @@ -49,7 +49,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "Anda hanya boleh mengimport dari format teks biasa atau html. Untuk ciri-ciri import yang lebih maju, sila memasang abiword.", "pad.modals.connected": "Bersambung.", "pad.modals.reconnecting": "Bersambung semula dengan pad anda...", diff --git a/src/locales/nap.json b/src/locales/nap.json index 6ba696fc..ffc7b740 100644 --- a/src/locales/nap.json +++ b/src/locales/nap.json @@ -9,14 +9,14 @@ "pad.toolbar.bold.title": "Grassetto (Ctrl-B)", "pad.toolbar.italic.title": "Cursivo (Ctrl-I)", "pad.toolbar.underline.title": "Sottolineato (Ctrl-U)", - "pad.toolbar.strikethrough.title": "Barrato", - "pad.toolbar.ol.title": "Ennece nummerato", - "pad.toolbar.ul.title": "Ennece puntato", + "pad.toolbar.strikethrough.title": "Barrato (Ctrl+5)", + "pad.toolbar.ol.title": "Ennece nummerato (Ctrl+Shift+N)", + "pad.toolbar.ul.title": "Ennece puntato (Ctrl+Shift+L)", "pad.toolbar.indent.title": "Rientro (TAB)", "pad.toolbar.unindent.title": "Riduce rientro (Shift+TAB)", "pad.toolbar.undo.title": "Annulla (Ctrl-Z)", "pad.toolbar.redo.title": "Ripete (Ctrl-Y)", - "pad.toolbar.clearAuthorship.title": "Elimina 'e culure ca 'ndicanno 'e auture", + "pad.toolbar.clearAuthorship.title": "Elimina 'e culure ca 'ndicanno 'e auture (Ctrl+Shift+C)", "pad.toolbar.import_export.title": "'Mporta/esporta 'e/a diverse furmate 'e file", "pad.toolbar.timeslider.title": "Presentazzione cronologgia", "pad.toolbar.savedRevision.title": "Sarva revisione", @@ -32,7 +32,6 @@ "pad.importExport.exporthtml": "HTML", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.modals.connected": "Cunnesso.", "pad.modals.reconnecting": "Ricunnessione ô pad 'n curso...", "pad.modals.forcereconnect": "Forza 'a ricunnessione", diff --git a/src/locales/nb.json b/src/locales/nb.json index 0620d2b4..a1b3a5ca 100644 --- a/src/locales/nb.json +++ b/src/locales/nb.json @@ -10,14 +10,14 @@ "pad.toolbar.bold.title": "Fet (Ctrl-B)", "pad.toolbar.italic.title": "Kursiv (Ctrl-I)", "pad.toolbar.underline.title": "Understreking (Ctrl-U)", - "pad.toolbar.strikethrough.title": "Gjennomstreking", - "pad.toolbar.ol.title": "Nummerert liste", - "pad.toolbar.ul.title": "Punktliste", + "pad.toolbar.strikethrough.title": "Gjennomstreking (Ctrl+5)", + "pad.toolbar.ol.title": "Nummerert liste (Ctrl+Shift+N)", + "pad.toolbar.ul.title": "Punktliste (Ctrl+Shift+L)", "pad.toolbar.indent.title": "Innrykk (TAB)", "pad.toolbar.unindent.title": "Rykk ut (Shift+TAB)", "pad.toolbar.undo.title": "Angre (Ctrl-Z)", "pad.toolbar.redo.title": "Gjør omigjen (Ctrl-Y)", - "pad.toolbar.clearAuthorship.title": "Fjern forfatterfarger", + "pad.toolbar.clearAuthorship.title": "Fjern forfatterfarger (Ctrl+Shift+C)", "pad.toolbar.import_export.title": "Importer/eksporter fra/til forskjellige filformater", "pad.toolbar.timeslider.title": "Tidslinje", "pad.toolbar.savedRevision.title": "Lagre revisjoner", @@ -50,7 +50,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "Du kan bare importere fra ren tekst eller HTML-formater. For mer avanserte importfunksjoner, installer abiword.", "pad.modals.connected": "Tilkoblet.", "pad.modals.reconnecting": "Kobler til din pad på nytt...", diff --git a/src/locales/nds.json b/src/locales/nds.json index 451a0211..dfb92278 100644 --- a/src/locales/nds.json +++ b/src/locales/nds.json @@ -50,7 +50,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "Se köönt blots wat vun Kloortext oder HTML-Stücken röverhalen. Mit köönt Se ok anner Saken röverhalen. Dorför mööt Se bidde abiword inrichten.", "pad.modals.connected": "Verbindung steiht.", "pad.modals.reconnecting": "En Verbindung wedder opboen ...", diff --git a/src/locales/ne.json b/src/locales/ne.json index 37194884..b380526e 100644 --- a/src/locales/ne.json +++ b/src/locales/ne.json @@ -48,7 +48,6 @@ "pad.importExport.exportword": "माइक्रोसफ्ट वर्ड", "pad.importExport.exportpdf": "पिडिएफ", "pad.importExport.exportopen": "ओडिएफ(खुल्ला कागजात ढाँचा)", - "pad.importExport.exportdokuwiki": "डकुविकि", "pad.modals.connected": "जोडीएको।", "pad.modals.reconnecting": "तपाईँको प्याडमा पुन: जडान गर्दै", "pad.modals.forcereconnect": "जडानको लागि जोडगर्ने", diff --git a/src/locales/nl.json b/src/locales/nl.json index 5a9d9fba..ed69527e 100644 --- a/src/locales/nl.json +++ b/src/locales/nl.json @@ -1,7 +1,9 @@ { "@metadata": { "authors": [ - "Siebrand" + "Siebrand", + "Macofe", + "Robin0van0der0vliet" ] }, "index.newPad": "Nieuw pad", @@ -9,14 +11,14 @@ "pad.toolbar.bold.title": "Vet (Ctrl-B)", "pad.toolbar.italic.title": "Cursief (Ctrl-I)", "pad.toolbar.underline.title": "Onderstrepen (Ctrl-U)", - "pad.toolbar.strikethrough.title": "Doorhalen", - "pad.toolbar.ol.title": "Geordende lijst", - "pad.toolbar.ul.title": "Ongeordende lijst", + "pad.toolbar.strikethrough.title": "Doorhalen (Ctrl+5)", + "pad.toolbar.ol.title": "Geordende lijst (Ctrl+Shift+N)", + "pad.toolbar.ul.title": "Ongeordende lijst (Ctrl+Shift+L)", "pad.toolbar.indent.title": "Inspringen (Tab)", "pad.toolbar.unindent.title": "Inspringing verkleinen (Shift+Tab)", "pad.toolbar.undo.title": "Ongedaan maken (Ctrl-Z)", "pad.toolbar.redo.title": "Opnieuw uitvoeren (Ctrl-Y)", - "pad.toolbar.clearAuthorship.title": "Kleuren auteurs wissen", + "pad.toolbar.clearAuthorship.title": "Kleuren auteurs wissen (Ctrl+Shift+C)", "pad.toolbar.import_export.title": "Naar/van andere opmaak exporteren/importeren", "pad.toolbar.timeslider.title": "Tijdlijn", "pad.toolbar.savedRevision.title": "Versie opslaan", @@ -49,7 +51,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "Pdf", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "U kunt alleen importeren vanuit platte tekst of een HTML-opmaak. Installeer abiword om meer geavanceerde importmogelijkheden te krijgen.", "pad.modals.connected": "Verbonden.", "pad.modals.reconnecting": "Opnieuw verbinding maken met uw pad...", diff --git a/src/locales/nn.json b/src/locales/nn.json index 104a17dc..dc6368ed 100644 --- a/src/locales/nn.json +++ b/src/locales/nn.json @@ -48,7 +48,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "Du kan berre importera frå rein tekst- eller HTML-format. Ver venleg og installer Abiword om du treng meir avanserte importfunksjonar.", "pad.modals.connected": "Tilkopla.", "pad.modals.reconnecting": "Gjenopprettar tilkoplinga til blokka di …", diff --git a/src/locales/oc.json b/src/locales/oc.json index 5921f4ff..54a375ba 100644 --- a/src/locales/oc.json +++ b/src/locales/oc.json @@ -49,7 +49,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "Podètz pas importar que de formats tèxte brut o html. Per de foncionalitats d'importacion mai evoluadas, installatz abiword.", "pad.modals.connected": "Connectat.", "pad.modals.reconnecting": "Reconnexion cap a vòstre Pad...", diff --git a/src/locales/os.json b/src/locales/os.json index 109dada7..941fe2b8 100644 --- a/src/locales/os.json +++ b/src/locales/os.json @@ -49,7 +49,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "Дӕ бон у импорт кӕнын ӕрмӕст хуымӕтӕг текст кӕнӕ html форматӕй. Лӕмбынӕг импорты миниуджытӕн, дӕ хорзӕхӕй, сӕвӕр abiword.", "pad.modals.connected": "Иугонд.", "pad.modals.reconnecting": "Дӕ документмӕ ногӕй иугонд цӕуы..", diff --git a/src/locales/pa.json b/src/locales/pa.json index 55ce5e6c..9e154e36 100644 --- a/src/locales/pa.json +++ b/src/locales/pa.json @@ -50,7 +50,6 @@ "pad.importExport.exportword": "ਮਾਈਕਰੋਸਾਫਟ ਵਰਡ", "pad.importExport.exportpdf": "ਪੀਡੀਐਫ", "pad.importExport.exportopen": "ODF (ਓਪਨ ਡੌਕੂਮੈਂਟ ਫਾਰਮੈਟ)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "ਤੁਸੀਂ ਸਿਰਫ਼ ਸਾਦੀਆਂ ਲਿਖਤੀ ਜਾਂ ਐੱਚ.ਟੀ.ਐੱਮ.ਐੱਲ. ਰੂਪ-ਰੇਖਾਵਾਂ ਤੋਂ ਦਰਾਮਦ ਕਰ ਸਕਦੇ ਹੋ। ਹੋਰ ਉੱਨਤ ਦਰਾਮਦੀ ਗੁਣਾਂ ਵਾਸਤੇ ਮਿਹਰਬਾਨੀ ਕਰਕੇ ਐਬੀਵਰਡ ਥਾਪੋ।", "pad.modals.connected": "ਕੁਨੈਕਟ ਹੈ।", "pad.modals.reconnecting": "..ਤੁਹਾਡੇ ਪੈਡ ਨਾਲ ਮੁੜ-ਕੁਨੈਕਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ", diff --git a/src/locales/pl.json b/src/locales/pl.json index 9e7be2fb..7e68ca84 100644 --- a/src/locales/pl.json +++ b/src/locales/pl.json @@ -4,7 +4,8 @@ "Rezonansowy", "Ty221", "WTM", - "Woytecr" + "Woytecr", + "Macofe" ] }, "index.newPad": "Nowy dokument", @@ -19,7 +20,7 @@ "pad.toolbar.unindent.title": "Wcięcie (Shift + TAB)", "pad.toolbar.undo.title": "Cofnij (Ctrl-Z)", "pad.toolbar.redo.title": "Ponów (Ctrl-Y)", - "pad.toolbar.clearAuthorship.title": "Usuń kolory autorów", + "pad.toolbar.clearAuthorship.title": "Usuń kolory autorów (Ctrl+Shift+C)", "pad.toolbar.import_export.title": "Import/eksport z/do różnych formatów plików", "pad.toolbar.timeslider.title": "Oś czasu", "pad.toolbar.savedRevision.title": "Zapisz wersję", @@ -52,7 +53,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "Możesz importować pliki tylko w formacie zwykłego tekstu lub html. Aby umożliwić bardziej zaawansowane funkcje importu, zainstaluj abiword.", "pad.modals.connected": "Połączony.", "pad.modals.reconnecting": "Ponowne łączenie z dokumentem...", diff --git a/src/locales/ps.json b/src/locales/ps.json index 76004412..1a57e76a 100644 --- a/src/locales/ps.json +++ b/src/locales/ps.json @@ -32,7 +32,6 @@ "pad.importExport.exportword": "مايکروسافټ ورډ", "pad.importExport.exportpdf": "پي ډي اېف", "pad.importExport.exportopen": "ODF (اوپن ډاکومنټ فارمټ)", - "pad.importExport.exportdokuwiki": "ډوکوويکي", "pad.modals.connected": "اړيکمن شو.", "pad.modals.slowcommit.explanation": "پالنگر ځواب نه وايي.", "pad.modals.slowcommit.cause": "دا کېدای شي د جال د اړيکتيايي ستونزو په سبب وي.", diff --git a/src/locales/pt-br.json b/src/locales/pt-br.json index 333d59fb..204d7792 100644 --- a/src/locales/pt-br.json +++ b/src/locales/pt-br.json @@ -8,7 +8,9 @@ "Titoncio", "Tuliouel", "Rafaelff", - "Dianakc" + "Dianakc", + "Macofe", + "Rodrigo codignoli" ] }, "index.newPad": "Nova Nota", @@ -16,14 +18,14 @@ "pad.toolbar.bold.title": "Negrito (Ctrl-B)", "pad.toolbar.italic.title": "Itálico (Ctrl-I)", "pad.toolbar.underline.title": "Sublinhar (Ctrl-U)", - "pad.toolbar.strikethrough.title": "Tachado", - "pad.toolbar.ol.title": "Lista ordenada", - "pad.toolbar.ul.title": "Lista não ordenada", + "pad.toolbar.strikethrough.title": "Tachado (Ctrl+5)", + "pad.toolbar.ol.title": "Lista ordenada (Ctrl+Shift+N)", + "pad.toolbar.ul.title": "Lista não ordenada (Ctrl+Shift+L)", "pad.toolbar.indent.title": "Aumentar Recuo (TAB)", "pad.toolbar.unindent.title": "Diminuir Recuo (Shift+TAB)", "pad.toolbar.undo.title": "Desfazer (Ctrl-Z)", "pad.toolbar.redo.title": "Refazer (Ctrl-Y)", - "pad.toolbar.clearAuthorship.title": "Limpar as cores de identificação de autoria", + "pad.toolbar.clearAuthorship.title": "Limpar as cores de identificação de autoria (Ctrl+Shift+C)", "pad.toolbar.import_export.title": "Importar/Exportar de/para diferentes formatos de arquivo", "pad.toolbar.timeslider.title": "Linha do tempo", "pad.toolbar.savedRevision.title": "Salvar revisão", @@ -56,7 +58,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "Você só pode importar de formatos de texto puro ou html. Para recursos de importação mais avançados instale o abiword.", "pad.modals.connected": "Conectado.", "pad.modals.reconnecting": "Reconectando à sua nota...", diff --git a/src/locales/pt.json b/src/locales/pt.json index 69a4dca1..c7cfcb5c 100644 --- a/src/locales/pt.json +++ b/src/locales/pt.json @@ -21,7 +21,7 @@ "pad.toolbar.unindent.title": "Recuar (Shift+TAB)", "pad.toolbar.undo.title": "Desfazer (Ctrl-Z)", "pad.toolbar.redo.title": "Refazer (Ctrl-Y)", - "pad.toolbar.clearAuthorship.title": "Limpar cores de autoria", + "pad.toolbar.clearAuthorship.title": "Limpar cores de autoria (Ctrl+Shift+C)", "pad.toolbar.import_export.title": "Importar/exportar de/para diferentes formatos de ficheiro", "pad.toolbar.timeslider.title": "Linha de tempo", "pad.toolbar.savedRevision.title": "Salvar revisão", @@ -54,7 +54,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.modals.connected": "Ligado.", "pad.modals.reconnecting": "Reconectando-se ao seu bloco…", "pad.modals.forcereconnect": "Forçar reconexão", diff --git a/src/locales/ru.json b/src/locales/ru.json index cd030bad..f96f2338 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -13,14 +13,14 @@ "pad.toolbar.bold.title": "полужирный (Ctrl-B)", "pad.toolbar.italic.title": "курсив (Ctrl-I)", "pad.toolbar.underline.title": "подчёркивание (Ctrl-U)", - "pad.toolbar.strikethrough.title": "зачёркивание", - "pad.toolbar.ol.title": "Упорядоченный список", - "pad.toolbar.ul.title": "Неупорядоченный список", + "pad.toolbar.strikethrough.title": "Зачёркивание (Ctrl+5)", + "pad.toolbar.ol.title": "Упорядоченный список (Ctrl+Shift+N)", + "pad.toolbar.ul.title": "Неупорядоченный список (Ctrl+Shift+L)", "pad.toolbar.indent.title": "Отступ (TAB)", "pad.toolbar.unindent.title": "Выступ (Shift+TAB)", "pad.toolbar.undo.title": "Отменить (Ctrl-Z)", "pad.toolbar.redo.title": "Вернуть (Ctrl-Y)", - "pad.toolbar.clearAuthorship.title": "Очистить цвета документа", + "pad.toolbar.clearAuthorship.title": "Очистить цвета документа (Ctrl+Shift+C)", "pad.toolbar.import_export.title": "Импорт/экспорт с использованием различных форматов файлов", "pad.toolbar.timeslider.title": "Шкала времени", "pad.toolbar.savedRevision.title": "Сохранить версию", @@ -53,7 +53,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (документ OpenOffice)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "Вы можете импортировать только из обычного текста или HTML. Для более продвинутых функций импорта, пожалуйста, установите AbiWord.", "pad.modals.connected": "Подключен.", "pad.modals.reconnecting": "Повторное подключение к вашему документу", diff --git a/src/locales/sco.json b/src/locales/sco.json index efcb187a..f7b01824 100644 --- a/src/locales/sco.json +++ b/src/locales/sco.json @@ -49,7 +49,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "Ye can yinly import fae plain tex or HTML formats. Fer mair advanced import features please install abiword.", "pad.modals.connected": "Connected.", "pad.modals.reconnecting": "Reconnectin til yer pad..", diff --git a/src/locales/sk.json b/src/locales/sk.json index 8f376926..02c5c978 100644 --- a/src/locales/sk.json +++ b/src/locales/sk.json @@ -51,7 +51,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "Importovať môžete len čistý text alebo HTML. Pre pokročilejšie funkcie importu prosím nainštalujte „Abiword“.", "pad.modals.connected": "Pripojené.", "pad.modals.reconnecting": "Opätovné pripájanie k vášmu Padu...", diff --git a/src/locales/sl.json b/src/locales/sl.json index 8e57cffa..41a6ce76 100644 --- a/src/locales/sl.json +++ b/src/locales/sl.json @@ -10,14 +10,14 @@ "pad.toolbar.bold.title": "Krepko (Ctrl-B)", "pad.toolbar.italic.title": "Ležeče (Ctrl-I)", "pad.toolbar.underline.title": "Podčrtano (Ctrl-U)", - "pad.toolbar.strikethrough.title": "Prečrtano", - "pad.toolbar.ol.title": "Oštevilčen seznam", - "pad.toolbar.ul.title": "Vrstični seznam", + "pad.toolbar.strikethrough.title": "Prečrtano (Ctrl+5)", + "pad.toolbar.ol.title": "Oštevilčen seznam (Ctrl+Shift+N)", + "pad.toolbar.ul.title": "Neurejen seznam (Ctrl+Shift+L)", "pad.toolbar.indent.title": "Zamik desno (TAB)", "pad.toolbar.unindent.title": "Zamik levo (Shift+TAB)", "pad.toolbar.undo.title": "Razveljavi (Ctrl-Z)", "pad.toolbar.redo.title": "Ponovno uveljavi (Ctrl-Y)", - "pad.toolbar.clearAuthorship.title": "Počisti barvo avtorstva", + "pad.toolbar.clearAuthorship.title": "Počisti barvo avtorstva (Ctrl+Shift+C)", "pad.toolbar.import_export.title": "Izvozi/Uvozi različne oblike zapisov", "pad.toolbar.timeslider.title": "Drsnik zgodovine", "pad.toolbar.savedRevision.title": "Shrani predelavo", @@ -50,7 +50,6 @@ "pad.importExport.exportword": "DOC (zapis Microsoft Word)", "pad.importExport.exportpdf": "PDF (zapis Acrobat PDF)", "pad.importExport.exportopen": "ODF (zapis Open Document)", - "pad.importExport.exportdokuwiki": "DokuWiki (zapis DokuWiki)", "pad.importExport.abiword.innerHTML": "Uvoziti je mogoče le običajno neoblikovano besedilo in zapise HTML. Za naprednejše zmožnosti namestite program Abiword.", "pad.modals.connected": "Povezano.", "pad.modals.reconnecting": "Poteka povezovanje z dokumentom ...", diff --git a/src/locales/sq.json b/src/locales/sq.json index 54e76067..6374ea62 100644 --- a/src/locales/sq.json +++ b/src/locales/sq.json @@ -9,17 +9,17 @@ "pad.toolbar.bold.title": "Të trasha (Ctrl-B)", "pad.toolbar.italic.title": "Të pjerrëta (Ctrl-I)", "pad.toolbar.underline.title": "Të nënvizuara (Ctrl-U)", - "pad.toolbar.strikethrough.title": "Hequrvije", - "pad.toolbar.ol.title": "Listë e renditur", - "pad.toolbar.ul.title": "Listë e parenditur", - "pad.toolbar.indent.title": "Brendazi", - "pad.toolbar.unindent.title": "Jashtazi", + "pad.toolbar.strikethrough.title": "Hequrvije (Ctrl+5)", + "pad.toolbar.ol.title": "Listë e renditur (Ctrl+Shift+N)", + "pad.toolbar.ul.title": "Listë e parenditur (Ctrl+Shift+L)", + "pad.toolbar.indent.title": "Brendazi (TAB)", + "pad.toolbar.unindent.title": "Jashtazi (Shift+TAB)", "pad.toolbar.undo.title": "Zhbëje (Ctrl-Z)", "pad.toolbar.redo.title": "Ribëje (Ctrl-Y)", - "pad.toolbar.clearAuthorship.title": "Hiq Ngjyra Autorësish", + "pad.toolbar.clearAuthorship.title": "Hiqju Ngjyra Autorësish (Ctrl+Shift+C)", "pad.toolbar.import_export.title": "Importoni/Eksportoni nga/në formate të tjera kartelash", "pad.toolbar.timeslider.title": "Rrjedha kohore", - "pad.toolbar.savedRevision.title": "Ruaje Rishikin", + "pad.toolbar.savedRevision.title": "Ruaje Rishikimin", "pad.toolbar.settings.title": "Rregullime", "pad.toolbar.embed.title": "Ndajeni me të tjerët dhe Trupëzojeni këtë bllok", "pad.toolbar.showusers.title": "Shfaq përdoruesit në këtë bllok", @@ -49,14 +49,13 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "Mund të importoni vetëm prej formati tekst i thjeshtë ose html. Për veçori më të përparuara importimi, ju lutemi, instaloni Abiword-in.", "pad.modals.connected": "I lidhur.", "pad.modals.reconnecting": "Po rilidheni te blloku juaj..", "pad.modals.forcereconnect": "Rilidhje e detyruar", "pad.modals.userdup": "Hapur në një tjetër dritare", "pad.modals.userdup.explanation": "Ky bllok duket se gjendet i hapur në më shumë se një dritare shfletuesi në këtë kompjuter.", - "pad.modals.userdup.advice": "Rilidhu që të përdoret kjo dritare, më mirë.", + "pad.modals.userdup.advice": "Rilidhuni që të përdoret kjo dritare.", "pad.modals.unauth": "I paautorizuar", "pad.modals.unauth.explanation": "Ndërkohë që shihnit këtë dritare, lejet tuaja kanë ndryshuar. Provoni të rilidheni.", "pad.modals.looping.explanation": "Ka probleme komunikimi me shërbyesin e njëkohësimit.", @@ -66,14 +65,14 @@ "pad.modals.initsocketfail.cause": "Ka gjasa që kjo vjen për shkak të një problemi me shfletuesin tuaj ose lidhjen tuaj në internet.", "pad.modals.slowcommit.explanation": "Shërbyesi nuk po përgjigjet.", "pad.modals.slowcommit.cause": "Kjo mund të vijë për shkak problemesh lidhjeje me rrjetin.", - "pad.modals.badChangeset.explanation": "Një përpunim që keni bërë u shpall i paligjshëm nga shërbyesu i njëkohësimit.", + "pad.modals.badChangeset.explanation": "Një përpunim që keni bërë u vlerësua si i paligjshëm nga shërbyesi i njëkohësimit.", "pad.modals.badChangeset.cause": "Kjo mund të jetë për shkak të një formësimi të gabuar të shërbyesit ose ndonjë tjetër sjelljeje të papritur. Ju lutemi, lidhuni me përgjegjësin e shërbimit, nëse mendoni që ky është një gabim. Provoni të rilidheni që të vazhdoni përpunimin.", "pad.modals.corruptPad.explanation": "Blloku te i cili po përpiqeni të hyni është i dëmtuar.", "pad.modals.corruptPad.cause": "Kjo mund të vijë nga një formësim i gabuar shërbyesi ose ndonjë tjetër sjellje e papritur. Ju lutemi, lidhuni me përgjegjësin e shërbimit.", "pad.modals.deleted": "I fshirë.", "pad.modals.deleted.explanation": "Ky bllok është hequr.", "pad.modals.disconnected": "Jeni shkëputur.", - "pad.modals.disconnected.explanation": "U pre lidhja me shërbyesin", + "pad.modals.disconnected.explanation": "U ndërpre lidhja me shërbyesin", "pad.modals.disconnected.cause": "Shërbyesi mund të mos jetë në punë. Ju lutemi, njoftoni përgjegjësin e shërbimit, nëse kjo vazhdon të ndodhë.", "pad.share": "Ndajeni këtë bllok me të tjerët", "pad.share.readonly": "Vetëm për lexim", @@ -103,11 +102,12 @@ "timeslider.month.october": "Tetor", "timeslider.month.november": "Nëntor", "timeslider.month.december": "Dhjetor", + "timeslider.unnamedauthors": "{{num}} i paemërt {[plural(num) një: autor, tjetër: autorë ]}", "pad.savedrevs.marked": "Ky rishikim tani është shënuar si rishikim i ruajtur", "pad.userlist.entername": "Jepni emrin tuaj", "pad.userlist.unnamed": "pa emër", "pad.userlist.guest": "Vizitor", - "pad.userlist.deny": "Mohoje", + "pad.userlist.deny": "Hidheni Tej", "pad.userlist.approve": "Miratoje", "pad.editbar.clearcolors": "Të hiqen ngjyra autorësish në krejt dokumentin?", "pad.impexp.importbutton": "Importoje Tani", diff --git a/src/locales/sv.json b/src/locales/sv.json index ab6f3820..bda3cb83 100644 --- a/src/locales/sv.json +++ b/src/locales/sv.json @@ -18,7 +18,7 @@ "pad.toolbar.unindent.title": "Minska indrag (Shift+TABB)", "pad.toolbar.undo.title": "Ångra (Ctrl-Z)", "pad.toolbar.redo.title": "Gör om (Ctrl-Y)", - "pad.toolbar.clearAuthorship.title": "Rensa författarfärger", + "pad.toolbar.clearAuthorship.title": "Rensa författarfärger (Ctrl+Shift+C)", "pad.toolbar.import_export.title": "Importera/exportera från/till olika filformat", "pad.toolbar.timeslider.title": "Tidsreglage", "pad.toolbar.savedRevision.title": "Spara version", @@ -51,7 +51,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "Du kan endast importera från oformaterad text eller HTML-format. För mer avancerade importeringsfunktioner, var god installera abiword.", "pad.modals.connected": "Ansluten.", "pad.modals.reconnecting": "Återansluter till ditt block...", diff --git a/src/locales/te.json b/src/locales/te.json index 41341d1e..19878206 100644 --- a/src/locales/te.json +++ b/src/locales/te.json @@ -50,7 +50,6 @@ "pad.importExport.exportword": "మైక్రోసాఫ్ట్ వర్డ్", "pad.importExport.exportpdf": "పీ డి ఎఫ్", "pad.importExport.exportopen": "ఓ డి ఎఫ్ (ఓపెన్ డాక్యుమెంట్ ఫార్మాట్)", - "pad.importExport.exportdokuwiki": "డాక్యువికి", "pad.modals.connected": "సంబంధం కుదిరింది.", "pad.modals.reconnecting": "మీ పలకకు మరల సంబంధం కలుపుతుంది...", "pad.modals.forcereconnect": "బలవంతంగానైనా సంబంధం కుదిరించు", diff --git a/src/locales/tr.json b/src/locales/tr.json index a83c7313..54030fda 100644 --- a/src/locales/tr.json +++ b/src/locales/tr.json @@ -20,7 +20,7 @@ "pad.toolbar.unindent.title": "Girintiyi azalt (Shift+TAB)", "pad.toolbar.undo.title": "Geri Al (Ctrl-Z)", "pad.toolbar.redo.title": "Yinele (Ctrl+Y)", - "pad.toolbar.clearAuthorship.title": "Yazarlık Renklerini Temizle", + "pad.toolbar.clearAuthorship.title": "Yazarlık Renklerini Temizle (Ctrl+Shift+C)", "pad.toolbar.import_export.title": "Farklı dosya biçimlerini içeri/dışarı aktar", "pad.toolbar.timeslider.title": "Zaman Çizelgesi", "pad.toolbar.savedRevision.title": "Düzeltmeyi Kaydet", @@ -53,7 +53,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Açık Doküman Biçimi)", - "pad.importExport.exportdokuwiki": "VikiBelge", "pad.importExport.abiword.innerHTML": "Yalnızca düz metin ya da HTML biçimlerini içe aktarabilirsiniz. Daha fazla gelişmiş içe aktarım özellikleri için AbiWord'ü yükleyin.", "pad.modals.connected": "Bağlandı.", "pad.modals.reconnecting": "Bloknotunuza tekrar bağlanılıyor...", diff --git a/src/locales/uk.json b/src/locales/uk.json index 41686c5e..79a39a33 100644 --- a/src/locales/uk.json +++ b/src/locales/uk.json @@ -53,7 +53,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (документ OpenOffice)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "Ви можете імпортувати лище формати простого тексту або html. Для більш просунутих способів імпорту встановіть abiword.", "pad.modals.connected": "З'єднано.", "pad.modals.reconnecting": "Перепідлючення до Вашого документу..", diff --git a/src/locales/vi.json b/src/locales/vi.json index 3d5cb66d..da0baf50 100644 --- a/src/locales/vi.json +++ b/src/locales/vi.json @@ -52,7 +52,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "Bạn chỉ có thể nhập vào từ văn bản thuần túy hay định dạng HTML. Nếu muốn có nhiều chức năng nhập hơn xin hãy cài đặt abiword.", "pad.modals.connected": "Đã kết nối lại.", "pad.modals.reconnecting": "Kết nối lại tới pad của bạn", diff --git a/src/locales/zh-hans.json b/src/locales/zh-hans.json index 61aa99fc..104e850b 100644 --- a/src/locales/zh-hans.json +++ b/src/locales/zh-hans.json @@ -25,7 +25,7 @@ "pad.toolbar.unindent.title": "减少缩进(Shift+TAB)", "pad.toolbar.undo.title": "撤消 (Ctrl-Z)", "pad.toolbar.redo.title": "重做 (Ctrl-Y)", - "pad.toolbar.clearAuthorship.title": "清除作者颜色", + "pad.toolbar.clearAuthorship.title": "清除作者颜色(Ctrl+Shift+C)", "pad.toolbar.import_export.title": "从不同的文件格式导入/导出", "pad.toolbar.timeslider.title": "时间轴", "pad.toolbar.savedRevision.title": "保存修订", @@ -58,7 +58,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF(开放文档格式)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "您只能导入纯文本或HTML格式。安裝abiword取得更多高级的导入功能。", "pad.modals.connected": "已连接。", "pad.modals.reconnecting": "重新连接到您的记事本...", diff --git a/src/locales/zh-hant.json b/src/locales/zh-hant.json index 3b084e3d..6d79268c 100644 --- a/src/locales/zh-hant.json +++ b/src/locales/zh-hant.json @@ -54,7 +54,6 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF(開放文件格式)", - "pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.abiword.innerHTML": "您只可以純文字或html格式檔匯入。安裝abiword取得更多進階的匯入功能。", "pad.modals.connected": "已連線。", "pad.modals.reconnecting": "重新連接到您的pad...", @@ -73,7 +72,7 @@ "pad.modals.slowcommit.cause": "這可能是因為網路連線問題所造成。", "pad.modals.badChangeset.explanation": "您的一個編輯被同步伺服器類為非法。", "pad.modals.badChangeset.cause": "這可能由於伺服器的配置錯誤或遇到意外問題。若您認為這是錯誤,請聯繫伺服器管理員。如要繼續編輯,請嘗試重新連接。", - "pad.modals.corruptPad.explanation": "您試圖訪問的平板已損壞。", + "pad.modals.corruptPad.explanation": "您試圖存取的平板已損壞。", "pad.modals.corruptPad.cause": "這可能由於伺服器的配置錯誤或遇到意外問題。請聯繫伺服器管理員。", "pad.modals.deleted": "已刪除。", "pad.modals.deleted.explanation": "此pad已被移除。", From 251a75346d8672b17f5168e9e1eb6bae66a3257a Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Sun, 14 Dec 2014 17:48:19 +0100 Subject: [PATCH 217/263] make stringIterator aware of newlines --- src/static/js/Changeset.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/static/js/Changeset.js b/src/static/js/Changeset.js index 366ad15f..629cf759 100644 --- a/src/static/js/Changeset.js +++ b/src/static/js/Changeset.js @@ -507,6 +507,10 @@ exports.opAssembler = function () { */ exports.stringIterator = function (str) { var curIndex = 0; + var newLines = str.split("\n").length - 1 + function getnewLines(){ + return newLines + } function assertRemaining(n) { exports.assert(n <= remaining(), "!(", n, " <= ", remaining(), ")"); @@ -515,6 +519,7 @@ exports.stringIterator = function (str) { function take(n) { assertRemaining(n); var s = str.substr(curIndex, n); + newLines -= s.split("\n").length - 1 curIndex += n; return s; } @@ -537,7 +542,8 @@ exports.stringIterator = function (str) { take: take, skip: skip, remaining: remaining, - peek: peek + peek: peek, + newlines: getnewLines }; }; From 51c14d994756e60333b0b60eccb7255cf0c86461 Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Sun, 14 Dec 2014 17:51:34 +0100 Subject: [PATCH 218/263] check if op.lines is in sync with atext-newlines Conflicts: src/static/js/Changeset.js --- src/static/js/Changeset.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/static/js/Changeset.js b/src/static/js/Changeset.js index 629cf759..acb59cc3 100644 --- a/src/static/js/Changeset.js +++ b/src/static/js/Changeset.js @@ -916,6 +916,8 @@ exports.applyToText = function (cs, str) { var csIter = exports.opIterator(unpacked.ops); var bankIter = exports.stringIterator(unpacked.charBank); var strIter = exports.stringIterator(str); + var newlines = 0 + var newlinefail = false var assem = exports.stringAssembler(); while (csIter.hasNext()) { var op = csIter.next(); @@ -925,16 +927,24 @@ exports.applyToText = function (cs, str) { break; case '-': removedLines += op.lines; + newlines = strIter.newlines() strIter.skip(op.chars); + if(!(newlines - strIter.newlines() == op.lines)){ + newlinefail = true + } break; case '=': + newlines = strIter.newlines() assem.append(strIter.take(op.chars)); + if(!(newlines - strIter.newlines() == op.lines)){ + newlinefail = true + } break; } } exports.assert(totalNrOfLines >= removedLines,"cannot remove ", removedLines, " lines from text with ", totalNrOfLines, " lines"); assem.append(strIter.take(strIter.remaining())); - return assem.toString(); + return [assem.toString(),newlinefail]; }; /** @@ -1605,8 +1615,12 @@ exports.makeAText = function (text, attribs) { * @param pool {AttribPool} Attribute Pool to add to */ exports.applyToAText = function (cs, atext, pool) { + var text = exports.applyToText(cs, atext.text) + if(text[1]){ + throw new Error() + } return { - text: exports.applyToText(cs, atext.text), + text: text[0], attribs: exports.applyToAttribution(cs, atext.attribs, pool) }; }; From 3354b9406b94e1a04b5eee1c0152914dde73ba89 Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Tue, 16 Dec 2014 12:29:33 +0100 Subject: [PATCH 219/263] op is ok, if it doesnt include newlines. op is ok, if op.lines is equal to newlines in op.chars --- src/static/js/Changeset.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/static/js/Changeset.js b/src/static/js/Changeset.js index acb59cc3..32da887d 100644 --- a/src/static/js/Changeset.js +++ b/src/static/js/Changeset.js @@ -929,7 +929,7 @@ exports.applyToText = function (cs, str) { removedLines += op.lines; newlines = strIter.newlines() strIter.skip(op.chars); - if(!(newlines - strIter.newlines() == op.lines)){ + if(!(newlines - strIter.newlines() == 0) && (newlines - strIter.newlines() != op.lines)){ newlinefail = true } break; From b1f29b914cbbbda9d14e212f1d272c92012cb1c4 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 27 Dec 2014 22:05:35 +0100 Subject: [PATCH 220/263] minor ts bg fix --- src/static/css/timeslider.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/static/css/timeslider.css b/src/static/css/timeslider.css index f4329a5e..e314831f 100644 --- a/src/static/css/timeslider.css +++ b/src/static/css/timeslider.css @@ -37,6 +37,8 @@ -moz-user-select: none; -ms-user-select: none; user-select: none; + background-color:#fff; + /* bgcolor is reuqired so you can't see pad content behind it */ } #timeslider #timeslider-slider { height: 61px; From 9cf19b99feaa9500a7805bdc63bad1cb022f981b Mon Sep 17 00:00:00 2001 From: Gared Date: Sat, 27 Dec 2014 22:25:24 +0100 Subject: [PATCH 221/263] Fix missing callback of #2400 and ignore missing author on timeslider on client-side --- src/node/handler/PadMessageHandler.js | 7 +++++- src/static/js/broadcast_slider.js | 33 +++++++++++++++------------ 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index 7cdefc65..5c8f9131 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -1020,7 +1020,12 @@ function handleClientReady(client, message) { authorManager.getAuthor(authorId, function(err, author) { - if(ERR(err, callback) || !author) return; + if(!author && !err) + { + messageLogger.error("There is no author for authorId:", authorId); + return callback(); + } + if(ERR(err, callback)) return; historicalAuthorData[authorId] = {name: author.name, colorId: author.colorId}; // Filter author attribs (e.g. don't send author's pads to all clients) callback(); }); diff --git a/src/static/js/broadcast_slider.js b/src/static/js/broadcast_slider.js index 822526a3..ea40e407 100644 --- a/src/static/js/broadcast_slider.js +++ b/src/static/js/broadcast_slider.js @@ -177,23 +177,26 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded) var colorsAnonymous = []; _.each(authors, function(author) { - var authorColor = clientVars.colorPalette[author.colorId] || author.colorId; - if (author.name) + if(author) { - if (numNamed !== 0) authorsList.append(', '); - - $('') - .text(author.name || "unnamed") - .css('background-color', authorColor) - .addClass('author') - .appendTo(authorsList); + var authorColor = clientVars.colorPalette[author.colorId] || author.colorId; + if (author.name) + { + if (numNamed !== 0) authorsList.append(', '); + + $('') + .text(author.name || "unnamed") + .css('background-color', authorColor) + .addClass('author') + .appendTo(authorsList); - numNamed++; - } - else - { - numAnonymous++; - if(authorColor) colorsAnonymous.push(authorColor); + numNamed++; + } + else + { + numAnonymous++; + if(authorColor) colorsAnonymous.push(authorColor); + } } }); if (numAnonymous > 0) From c276343216dc02df1783c07f9e101a2ec3a107b8 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 27 Dec 2014 22:31:23 +0100 Subject: [PATCH 222/263] timeslider fixes --- src/static/css/timeslider.css | 12 +++++++++--- src/static/js/broadcast_slider.js | 3 +++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/static/css/timeslider.css b/src/static/css/timeslider.css index e314831f..4c84a7fc 100644 --- a/src/static/css/timeslider.css +++ b/src/static/css/timeslider.css @@ -188,8 +188,15 @@ stepper:active{ -ms-user-select: none; user-select: none; } -#editbarright { - float: right +.editbarright { + float: right; + text-align: right; + height: 30px !important; +} +.toolbar ul{ + position:relative; + float:right; + height:30px; } #settings, #import_export, @@ -224,7 +231,6 @@ stepper:active{ border-bottom: none; float: right; width: 170px; - width: initial; } .timeslider-bar h1 { margin: 5px diff --git a/src/static/js/broadcast_slider.js b/src/static/js/broadcast_slider.js index 822526a3..344a7b54 100644 --- a/src/static/js/broadcast_slider.js +++ b/src/static/js/broadcast_slider.js @@ -29,6 +29,9 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded) { var BroadcastSlider; + // Hack to ensure timeslider i18n values are in + $("[data-key='timeslider_returnToPad'] > a > span").html(html10n.get("timeslider.toolbar.returnbutton")); + (function() { // wrap this code in its own namespace var sliderLength = 1000; From 53ca26d030649f3998a5bc5b3eb5953afa5eff8e Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Sun, 28 Dec 2014 02:27:53 +0100 Subject: [PATCH 223/263] add some import-export tests --- tests/frontend/specs/importexport.js | 249 +++++++++++++++++++++++++++ 1 file changed, 249 insertions(+) create mode 100644 tests/frontend/specs/importexport.js diff --git a/tests/frontend/specs/importexport.js b/tests/frontend/specs/importexport.js new file mode 100644 index 00000000..5c642ae3 --- /dev/null +++ b/tests/frontend/specs/importexport.js @@ -0,0 +1,249 @@ +describe("import functionality", function(){ + beforeEach(function(cb){ + helper.newPad(cb); // creates a new pad + this.timeout(60000); + }); + + function getinnertext(){ + var inner = helper.padInner$ + var newtext = "" + inner("div").each(function(line,el){ + newtext += el.innerHTML+"\n" + }) + return newtext + } + function importrequest(data,importurl,type){ + var success; + var error; + var result = $.ajax({ + url: importurl, + type: "post", + processData: false, + async: false, + contentType: 'multipart/form-data; boundary=boundary', + accepts: { + text: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" + }, + data: 'Content-Type: multipart/form-data; boundary=--boundary\r\n\r\n--boundary\r\nContent-Disposition: form-data; name="file"; filename="import.'+type+'"\r\nContent-Type: text/plain\r\n\r\n' + data + '\r\n\r\n--boundary', + error: function(res){ + error = res + } + }) + expect(error).to.be(undefined) + return result + } + function exportfunc(link){ + var exportresults = [] + $.ajaxSetup({ + async:false + }) + $.get(link+"/export/html",function(data){ + var start = data.indexOf("") + var end = data.indexOf("") + var html = data.substr(start+6,end-start-6) + exportresults.push(["html",html]) + }) + $.get(link+"/export/txt",function(data){ + exportresults.push(["txt",data]) + }) + return exportresults + } + + it("import a pad with newlines from txt", function(done){ + var importurl = helper.padChrome$.window.location.href+'/import' + var textWithNewLines = 'imported text\nnewline' + importrequest(textWithNewLines,importurl,"txt") + helper.waitFor(function(){ + return expect(getinnertext()).to.be('imported text\nnewline\n
\n') + }) + var results = exportfunc(helper.padChrome$.window.location.href) + expect(results[0][1]).to.be("imported text
newline

") + expect(results[1][1]).to.be("imported text\nnewline\n\n") + done() + }) + it("import a pad with newlines from html", function(done){ + var importurl = helper.padChrome$.window.location.href+'/import' + var htmlWithNewLines = 'htmltext
newline' + importrequest(htmlWithNewLines,importurl,"html") + helper.waitFor(function(){ + return expect(getinnertext()).to.be('htmltext\nnewline\n
\n') + }) + var results = exportfunc(helper.padChrome$.window.location.href) + expect(results[0][1]).to.be("htmltext
newline

") + expect(results[1][1]).to.be("htmltext\nnewline\n\n") + done() + }) + it("import a pad with attributes from html", function(done){ + var importurl = helper.padChrome$.window.location.href+'/import' + var htmlWithNewLines = 'htmltext
newline' + importrequest(htmlWithNewLines,importurl,"html") + helper.waitFor(function(){ + return expect(getinnertext()).to.be('htmltext\nnewline\n
\n') + }) + var results = exportfunc(helper.padChrome$.window.location.href) + expect(results[0][1]).to.be('htmltext
newline

') + expect(results[1][1]).to.be('htmltext\nnewline\n\n') + done() + }) + it("import a pad with bullets from html", function(done){ + var importurl = helper.padChrome$.window.location.href+'/import' + var htmlWithBullets = '
  • bullet line 1
  • bullet line 2
    • bullet2 line 1
    • bullet2 line 2
' + importrequest(htmlWithBullets,importurl,"html") + helper.waitFor(function(){ + return expect(getinnertext()).to.be('\ +
  • bullet line 1
\n\ +
  • bullet line 2
\n\ +
  • bullet2 line 1
\n\ +
  • bullet2 line 2
\n\ +
\n') + }) + var results = exportfunc(helper.padChrome$.window.location.href) + expect(results[0][1]).to.be('
  • bullet line 1
  • bullet line 2
    • bullet2 line 1
    • bullet2 line 2

') + expect(results[1][1]).to.be('\t* bullet line 1\n\t* bullet line 2\n\t\t* bullet2 line 1\n\t\t* bullet2 line 2\n\n') + done() + }) + it("import a pad with bullets and newlines from html", function(done){ + var importurl = helper.padChrome$.window.location.href+'/import' + var htmlWithBullets = '
  • bullet line 1

  • bullet line 2
    • bullet2 line 1

    • bullet2 line 2
' + importrequest(htmlWithBullets,importurl,"html") + helper.waitFor(function(){ + return expect(getinnertext()).to.be('\ +
  • bullet line 1
\n\ +
\n\ +
  • bullet line 2
\n\ +
  • bullet2 line 1
\n\ +
\n\ +
  • bullet2 line 2
\n\ +
\n') + }) + var results = exportfunc(helper.padChrome$.window.location.href) + expect(results[0][1]).to.be('
  • bullet line 1

  • bullet line 2
    • bullet2 line 1

    • bullet2 line 2

') + expect(results[1][1]).to.be('\t* bullet line 1\n\n\t* bullet line 2\n\t\t* bullet2 line 1\n\n\t\t* bullet2 line 2\n\n') + done() + }) + it("import a pad with bullets and newlines and attributes from html", function(done){ + var importurl = helper.padChrome$.window.location.href+'/import' + var htmlWithBullets = '
  • bullet line 1

  • bullet line 2
    • bullet2 line 1

        • bullet4 line 2 bisu
        • bullet4 line 2 bs
        • bullet4 line 2 uuis
' + importrequest(htmlWithBullets,importurl,"html") + helper.waitFor(function(){ + return expect(getinnertext()).to.be('\ +
  • bullet line 1
\n\
\n\ +
  • bullet line 2
\n\ +
  • bullet2 line 1
\n
\n\ +
  • bullet4 line 2 bisu
\n\ +
  • bullet4 line 2 bs
\n\ +
  • bullet4 line 2 uuis
\n\ +
\n') + }) + var results = exportfunc(helper.padChrome$.window.location.href) + expect(results[0][1]).to.be('
  • bullet line 1

  • bullet line 2
    • bullet2 line 1

        • bullet4 line 2 bisu
        • bullet4 line 2 bs
        • bullet4 line 2 uuis

') + expect(results[1][1]).to.be('\t* bullet line 1\n\n\t* bullet line 2\n\t\t* bullet2 line 1\n\n\t\t\t\t* bullet4 line 2 bisu\n\t\t\t\t* bullet4 line 2 bs\n\t\t\t\t* bullet4 line 2 uuis\n\n') + done() + }) + + + //RESULT IS WRONG AT THE LAST LINE + xit("import a pad with 8 levels of bullets and newlines and attributes from html", function(done){ + var importurl = helper.padChrome$.window.location.href+'/import' + var htmlWithBullets = '
  • bullet line 1

  • bullet line 2
    • bullet2 line 1

        • bullet4 line 2 bisu
        • bullet4 line 2 bs
        • bullet4 line 2 uuis
                • foo
                • foobar bs
          • foobar
    ' + importrequest(htmlWithBullets,importurl,"html") + helper.waitFor(function(){ + return expect(getinnertext()).to.be('\ +
    • bullet line 1
    \n\
    \n\ +
    • bullet line 2
    \n\ +
    • bullet2 line 1
    \n
    \n\ +
    • bullet4 line 2 bisu
    \n\ +
    • bullet4 line 2 bs
    \n\ +
    • bullet4 line 2 uuis
    \n\ +
    • foo
    \n\ +
    • foobar bs
    \n\ +
    • foobar
    \n\ +
    \n') + }) + var results = exportfunc(helper.padChrome$.window.location.href) + expect(results[0][1]).to.be('
    • bullet line 1

    • bullet line 2
      • bullet2 line 1

          • bullet4 line 2 bisu
          • bullet4 line 2 bs
          • bullet4 line 2 uuis
                  • foo
                  • foobar bs
                  • foobar

    ') + expect(results[1][1]).to.be('\t* bullet line 1\n\n\t* bullet line 2\n\t\t* bullet2 line 1\n\n\t\t\t\t* bullet4 line 2 bisu\n\t\t\t\t* bullet4 line 2 bs\n\t\t\t\t* bullet4 line 2 uuis\n\t\t\t\t\t\t\t\t* foo\n\t\t\t\t\t\t\t\t* foobar bs\n\t\t\t\t\t* foobar\n\n') + done() + }) + + xit("import a pad with indents from html", function(done){ + var importurl = helper.padChrome$.window.location.href+'/import' + var htmlWithIndents = '
    • indent line 1
    • indent line 2
      • indent2 line 1
      • indent2 line 2
    ' + importrequest(htmlWithIndents,importurl,"html") + console.error(getinnertext()) + expect(getinnertext()).to.be('\ +
    • indent line 1
    \n\ +
    • indent line 2
    \n\ +
    • indent2 line 1
    \n\ +
    • indent2 line 2
    \n\ +
    \n') + var results = exportfunc(helper.padChrome$.window.location.href) + console.error(results[0][1]) + expect(results[0][1]).to.be('
    • indent line 1
    • indent line 2
      • indent2 line 1
      • indent2 line 2
    ') + expect(results[1][1]).to.be('') + done() + }) + xit("import a pad with indented lists and newlines from html", function(done){ + var importurl = helper.padChrome$.window.location.href+'/import' + var htmlWithIndents = '
    • indent line 1

    • indent 10 line 2
      • indent 2 times line 1

      • indent 2 times line 2
    ' + importrequest(htmlWithIndents,importurl,"html") + expect(getinnertext()).to.be('\ +
    • indent line 1
    \n\ +
    \n\ +
    • indent 10 line 2
    \n\ +
    • indent 2 times line 1
    \n\ +
    \n\ +
    • indent 2 times line 2
    \n\ +
    \n') + var results = exportfunc(helper.padChrome$.window.location.href) + expect(results[0][1]).to.be('') + expect(results[1][1]).to.be('') + done() + }) + xit("import a pad with ordered lists from html", function(done){ + var importurl = helper.padChrome$.window.location.href+'/import' + var htmlWithBullets = '
    1. number 1 line 1
    1. number 2 line 2
    ' + importrequest(htmlWithBullets,importurl,"html") + -console.error(getinnertext()) + expect(getinnertext()).to.be('\ +
    1. number 1 line 1
    \n\ +
    1. number 2 line 2
    \n\ +
    \n') + var results = exportfunc(helper.padChrome$.window.location.href) + expect(results[0][1]).to.be('
    1. number 1 line 1
    1. number 2 line 2
    ') + expect(results[1][1]).to.be('') + done() + }) + xit("import a pad with ordered lists and newlines from html", function(done){ + var importurl = helper.padChrome$.window.location.href+'/import' + var htmlWithBullets = '
    1. number 9 line 1

    1. number 10 line 2
      1. number 2 times line 1

      1. number 2 times line 2
    ' + importrequest(htmlWithBullets,importurl,"html") + expect(getinnertext()).to.be('\ +
    1. number 9 line 1
    \n\ +
    \n\ +
    1. number 10 line 2
    \n\ +
    1. number 2 times line 1
    \n\ +
    \n\ +
    1. number 2 times line 2
    \n\ +
    \n') + var results = exportfunc(helper.padChrome$.window.location.href) + console.error(results) + done() + }) + xit("import a pad with nested ordered lists and attributes and newlines from html", function(done){ + var importurl = helper.padChrome$.window.location.href+'/import' + var htmlWithBullets = '
    1. bold strikethrough italics underline line 1bold

    1. number 10 line 2
      1. number 2 times line 1

      1. number 2 times line 2
    ' + importrequest(htmlWithBullets,importurl,"html") + expect(getinnertext()).to.be('\ +
    1. bold strikethrough italics underline line 1bold
    \n\ +
    \n\ +
    1. number 10 line 2
    \n\ +
    1. number 2 times line 1
    \n\ +
    \n\ +
    1. number 2 times line 2
    \n\ +
    \n') + var results = exportfunc(helper.padChrome$.window.location.href) + console.error(results) + done() + }) +}) From d71b11f4b2845f82018184ad0a2383d2147fcf8c Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Sun, 28 Dec 2014 02:31:26 +0100 Subject: [PATCH 224/263] fix export of lists with bullets;
  • are closed before the next list starts. also, when closing, ensure that list-items are not nested --- src/node/utils/ExportHtml.js | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/node/utils/ExportHtml.js b/src/node/utils/ExportHtml.js index 85d5e7a2..3e42b88b 100644 --- a/src/node/utils/ExportHtml.js +++ b/src/node/utils/ExportHtml.js @@ -328,6 +328,9 @@ function getHTMLFromAtext(pad, atext, authorColors) if (whichList >= lists.length)//means we are on a deeper level of indentation than the previous line { + if(lists.length > 0){ + pieces.push('
  • ') + } lists.push([line.listLevel, line.listTypeName]); if(line.listTypeName == "number") { @@ -363,7 +366,7 @@ function getHTMLFromAtext(pad, atext, authorColors) pieces.push('

    '); } }*/ - else//means we are getting closer to the lowest level of indentation + else//means we are getting closer to the lowest level of indentation or are at the same level { while (whichList < lists.length - 1) { @@ -382,15 +385,23 @@ function getHTMLFromAtext(pad, atext, authorColors) } else//outside any list { + if(lists.length > 0){ + if(lists[lists.length - 1][1] == "number"){ + pieces.push(''); + } else { + pieces.push('
'); + } + lists.length--; + } while (lists.length > 0)//if was in a list: close it before { if(lists[lists.length - 1][1] == "number") { - pieces.push(''); + pieces.push(''); } else { - pieces.push(''); + pieces.push(''); } lists.length--; } From 6ca2c91fdf412358ffb71a1bf298bb8705b96308 Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Sun, 28 Dec 2014 02:32:53 +0100 Subject: [PATCH 225/263] close and open lists according to the actual line.listLevel --- src/node/utils/ExportHtml.js | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/node/utils/ExportHtml.js b/src/node/utils/ExportHtml.js index 3e42b88b..bb29b34d 100644 --- a/src/node/utils/ExportHtml.js +++ b/src/node/utils/ExportHtml.js @@ -332,12 +332,23 @@ function getHTMLFromAtext(pad, atext, authorColors) pieces.push('') } lists.push([line.listLevel, line.listTypeName]); + + // if there is a previous list we need to open x tags, where x is the difference of the levels + // if there is no previous list we need to open x tags, where x is the wanted level + var toOpen = lists.length > 1 ? line.listLevel - lists[lists.length - 2][0] - 1 : line.listLevel - 1 + if(line.listTypeName == "number") { + if(toOpen > 0){ + pieces.push(new Array(toOpen + 1).join('
    ')) + } pieces.push('
    1. ', lineContent || '
      '); } else { + if(toOpen > 0){ + pieces.push(new Array(toOpen + 1).join('
        ')) + } pieces.push('
        • ', lineContent || '
          '); } } @@ -383,28 +394,19 @@ function getHTMLFromAtext(pad, atext, authorColors) pieces.push('
        • ', lineContent || '
          '); } } - else//outside any list + else//outside any list, need to close line.listLevel of lists { if(lists.length > 0){ if(lists[lists.length - 1][1] == "number"){ pieces.push('
    '); + pieces.push(new Array(lists[lists.length - 1][0]).join('
')) } else { pieces.push(''); + pieces.push(new Array(lists[lists.length - 1][0]).join('')) } - lists.length--; - } - while (lists.length > 0)//if was in a list: close it before - { - if(lists[lists.length - 1][1] == "number") - { - pieces.push(''); - } - else - { - pieces.push(''); - } - lists.length--; } + lists = [] + var lineContentFromHook = hooks.callAllStr("getLineHTMLForExport", { line: line, From 362315c638d2e6f682098635028c7aecdd9319f3 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Sun, 28 Dec 2014 12:34:42 +0100 Subject: [PATCH 226/263] AttribManager#removeAttribOnLine: Remove Linemarker entirely if there's nothing else left --- src/static/js/AttributeManager.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/static/js/AttributeManager.js b/src/static/js/AttributeManager.js index b1f6b421..1da1056a 100644 --- a/src/static/js/AttributeManager.js +++ b/src/static/js/AttributeManager.js @@ -172,7 +172,9 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ if(hasMarker){ ChangesetUtils.buildKeepRange(this.rep, builder, loc, (loc = [lineNum, 0])); - ChangesetUtils.buildKeepRange(this.rep, builder, loc, (loc = [lineNum, 1]), attribs, this.rep.apool); + // If length == 4, there's [author, lmkr, insertorder, + the attrib being removed] thus we can remove the marker entirely + if(attribs.length == 4) ChangesetUtils.buildRemoveRange(this.rep, builder, loc, (loc = [lineNum, 1])) + else ChangesetUtils.buildKeepRange(this.rep, builder, loc, (loc = [lineNum, 1]), attribs, this.rep.apool); } return this.applyChangeset(builder); From 2307c6fbda8ef149aebf83d47d099ca6d94f7ffc Mon Sep 17 00:00:00 2001 From: Gared Date: Sun, 28 Dec 2014 15:02:56 +0100 Subject: [PATCH 227/263] Notify user if cookies can't be created --- src/locales/en.json | 1 + src/static/css/pad.css | 4 ++++ src/static/js/pad.js | 10 ++++++++++ src/templates/pad.html | 3 +++ 4 files changed, 18 insertions(+) diff --git a/src/locales/en.json b/src/locales/en.json index 9a5fe45f..cea31263 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -24,6 +24,7 @@ "pad.colorpicker.cancel": "Cancel", "pad.loading": "Loading...", + "pad.nocookie": "Cookie could not be found. Please allow cookies in your browser!", "pad.passwordRequired": "You need a password to access this pad", "pad.permissionDenied": "You do not have permission to access this pad", "pad.wrongPassword": "Your password was wrong", diff --git a/src/static/css/pad.css b/src/static/css/pad.css index c1035e8d..b163a9a4 100644 --- a/src/static/css/pad.css +++ b/src/static/css/pad.css @@ -1086,6 +1086,10 @@ input[type=checkbox] { display:none; } +#nocookie{ + display:none; +} + /* gritter stuff */ #gritter-notice-wrapper { position:fixed; diff --git a/src/static/js/pad.js b/src/static/js/pad.js index 89ebfa76..6ca9e142 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.js @@ -442,6 +442,16 @@ var pad = { if (typeof customStart == "function") customStart(); getParams(); handshake(); + + // To use etherpad you have to allow cookies. + // This will check if the creation of a test-cookie has success. + // Otherwise it shows up a message to the user. + createCookie("test", "test"); + if (!readCookie("test")) + { + $('#loading').hide(); + $('#nocookie').show(); + } }); }, _afterHandshake: function() diff --git a/src/templates/pad.html b/src/templates/pad.html index 2dd66aa9..c6d48cd3 100644 --- a/src/templates/pad.html +++ b/src/templates/pad.html @@ -114,6 +114,9 @@

Your password was wrong

+
+

Cookie could not be found. Please allow cookies in your browser!

+
<% e.begin_block("loading"); %>

Loading...

<% e.end_block(); %> From 9715e039cdc1e98a872cdb9d4dad36f16498add3 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 28 Dec 2014 20:25:46 +0100 Subject: [PATCH 228/263] tests front chat going from stiky to smally --- tests/frontend/specs/chat.js | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/frontend/specs/chat.js b/tests/frontend/specs/chat.js index ccb8b569..7ebb76fb 100644 --- a/tests/frontend/specs/chat.js +++ b/tests/frontend/specs/chat.js @@ -96,4 +96,35 @@ describe("Chat messages and UI", function(){ done(); }); + + it("makes chat stick to right side of the screen then makes it one step smaller", function(done) { + var inner$ = helper.padInner$; + var chrome$ = helper.padChrome$; + + //click on the settings button to make settings visible + var $settingsButton = chrome$(".buttonicon-settings"); + $settingsButton.click(); + + //get the chat selector + var $stickychatCheckbox = chrome$("#options-stickychat"); + + //select chat always on screen and fire change event + $stickychatCheckbox.attr('selected','selected'); + $stickychatCheckbox.change(); + $stickychatCheckbox.click(); + + //check if chat changed to get the stickychat Class + var $chatbox = chrome$("#chatbox"); + var hasStickyChatClass = $chatbox.hasClass("stickyChat"); + expect(hasStickyChatClass).to.be(true); + + //select chat always on screen and fire change event + chrome$('#titlecross').click(); + + //check if chat changed to remove the stickychat Class + var hasStickyChatClass = $chatbox.hasClass("stickyChat"); + expect(hasStickyChatClass).to.be(false); + + done(); + }); }); From 018821e98c3e315eb37e55c05e743dc1268d04ad Mon Sep 17 00:00:00 2001 From: Gared Date: Sun, 28 Dec 2014 22:20:09 +0100 Subject: [PATCH 229/263] Use camel case --- src/locales/en.json | 2 +- src/static/css/pad.css | 2 +- src/static/js/pad.js | 2 +- src/templates/pad.html | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/locales/en.json b/src/locales/en.json index cea31263..130b59cb 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -24,7 +24,7 @@ "pad.colorpicker.cancel": "Cancel", "pad.loading": "Loading...", - "pad.nocookie": "Cookie could not be found. Please allow cookies in your browser!", + "pad.noCookie": "Cookie could not be found. Please allow cookies in your browser!", "pad.passwordRequired": "You need a password to access this pad", "pad.permissionDenied": "You do not have permission to access this pad", "pad.wrongPassword": "Your password was wrong", diff --git a/src/static/css/pad.css b/src/static/css/pad.css index b163a9a4..f1f859c1 100644 --- a/src/static/css/pad.css +++ b/src/static/css/pad.css @@ -1086,7 +1086,7 @@ input[type=checkbox] { display:none; } -#nocookie{ +#noCookie{ display:none; } diff --git a/src/static/js/pad.js b/src/static/js/pad.js index 6ca9e142..96c6f330 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.js @@ -450,7 +450,7 @@ var pad = { if (!readCookie("test")) { $('#loading').hide(); - $('#nocookie').show(); + $('#noCookie').show(); } }); }, diff --git a/src/templates/pad.html b/src/templates/pad.html index c6d48cd3..e442ef00 100644 --- a/src/templates/pad.html +++ b/src/templates/pad.html @@ -114,8 +114,8 @@

Your password was wrong

-
-

Cookie could not be found. Please allow cookies in your browser!

+
+

Cookie could not be found. Please allow cookies in your browser!

<% e.begin_block("loading"); %>

Loading...

From cec9065df8bb4c6b7a9d091090c4bc06965b7bc5 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 29 Dec 2014 00:30:11 +0100 Subject: [PATCH 230/263] test for #2395 #2402 --- tests/frontend/specs/unordered_list.js | 35 ++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 tests/frontend/specs/unordered_list.js diff --git a/tests/frontend/specs/unordered_list.js b/tests/frontend/specs/unordered_list.js new file mode 100644 index 00000000..4ea77b8a --- /dev/null +++ b/tests/frontend/specs/unordered_list.js @@ -0,0 +1,35 @@ +describe("assign unordered list", function(){ + //create a new pad before each test run + beforeEach(function(cb){ + helper.newPad(cb); + this.timeout(60000); + }); + + it("insert unordered list text then removes by outdent", function(done){ + var inner$ = helper.padInner$; + var chrome$ = helper.padChrome$; + var originalText = inner$("div").first().text(); + + var $insertunorderedlistButton = chrome$(".buttonicon-insertunorderedlist"); + $insertunorderedlistButton.click(); + + helper.waitFor(function(){ + var newText = inner$("div").first().text(); + if(newText === originalText){ + return inner$("div").first().find("ul li").length === 1; + } + }).done(function(){ + + // remove indentation by bullet and ensure text string remains the same + chrome$(".buttonicon-outdent").click(); + helper.waitFor(function(){ + var newText = inner$("div").first().text(); + return (newText === originalText); + }).done(function(){ + done(); + }); + + }); + }); + +}); From 8603fb458327ce40bcc4712862ddfcc00015ea16 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 29 Dec 2014 00:46:47 +0100 Subject: [PATCH 231/263] fix sockets of switch to pad --- src/node/handler/PadMessageHandler.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index 60880d57..46e48b66 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -902,7 +902,13 @@ function handleSwitchToPad(client, message) // clear the session and leave the room var currentSession = sessioninfos[client.id]; var padId = currentSession.padId; - var roomClients = socketio.sockets.clients(padId); + var roomClients = [], room = socketio.sockets.adapter.rooms[padId]; + if (room) { + for (var id in room) { + roomClients.push(socketio.sockets.adapter.nsp.connected[id]); + } + } + for(var i = 0; i < roomClients.length; i++) { var sinfo = sessioninfos[roomClients[i].id]; if(sinfo && sinfo.author == currentSession.author) { From 23570e80a7044493ee8c4de871455dd1c2072849 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 29 Dec 2014 01:17:24 +0100 Subject: [PATCH 232/263] unbind before rebind, partial fix towards #2413 issue --- src/static/js/pad_editbar.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/static/js/pad_editbar.js b/src/static/js/pad_editbar.js index 7e750a3e..e874614f 100644 --- a/src/static/js/pad_editbar.js +++ b/src/static/js/pad_editbar.js @@ -149,6 +149,7 @@ var padeditbar = (function() $("#editbar .editbarbutton").attr("unselectable", "on"); // for IE $("#editbar").removeClass("disabledtoolbar").addClass("enabledtoolbar"); $("#editbar [data-key]").each(function () { + $(this).unbind("click"); (new ToolbarItem($(this))).bind(function (command, item) { self.triggerCommand(command, item); }); From 321d550d432c8d3531d77a06bf668a9589c93b20 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 29 Dec 2014 01:20:28 +0100 Subject: [PATCH 233/263] second fix for user list issue on #2413 --- src/static/js/pad_userlist.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/static/js/pad_userlist.js b/src/static/js/pad_userlist.js index 595de92a..d306256a 100644 --- a/src/static/js/pad_userlist.js +++ b/src/static/js/pad_userlist.js @@ -468,7 +468,7 @@ var paduserlist = (function() self.setMyUserInfo(myInitialUserInfo); - $('#editbar [data-key=showusers] > a').append('1'); + if($('#online_count').length === 0) $('#editbar [data-key=showusers] > a').append('1'); $("#otheruserstable tr").remove(); From 302ceb665b2dd123d8c67efe05773646ab7aebbf Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 29 Dec 2014 14:59:22 +0100 Subject: [PATCH 234/263] delay write to fix copypad -- bad practice but due to db.set not allowing callback --- src/node/db/Pad.js | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/node/db/Pad.js b/src/node/db/Pad.js index 94049ff7..2f5860f8 100644 --- a/src/node/db/Pad.js +++ b/src/node/db/Pad.js @@ -461,7 +461,6 @@ Pad.prototype.copy = function copy(destinationID, force, callback) { // if the pad exists, we should abort, unless forced. function(callback) { - console.log("destinationID", destinationID, force); padManager.doesPadExists(destinationID, function (err, exists) { if(ERR(err, callback)) return; @@ -470,9 +469,9 @@ Pad.prototype.copy = function copy(destinationID, force, callback) { { if (!force) { - console.log("erroring out without force"); + console.error("erroring out without force"); callback(new customError("destinationID already exists","apierror")); - console.log("erroring out without force - after"); + console.error("erroring out without force - after"); return; } else // exists and forcing @@ -521,12 +520,9 @@ Pad.prototype.copy = function copy(destinationID, force, callback) { function(callback) { var revHead = _this.head; - //console.log(revHead); for(var i=0;i<=revHead;i++) { db.get("pad:"+sourceID+":revs:"+i, function (err, rev) { - //console.log("HERE"); - if (ERR(err, callback)) return; db.set("pad:"+destinationID+":revs:"+i, rev); }); @@ -538,10 +534,8 @@ Pad.prototype.copy = function copy(destinationID, force, callback) { function(callback) { var authorIDs = _this.getAllAuthors(); - authorIDs.forEach(function (authorID) { - console.log("authors"); authorManager.addPad(authorID, destinationID); }); @@ -555,7 +549,9 @@ Pad.prototype.copy = function copy(destinationID, force, callback) { if(destGroupID) db.setSub("group:" + destGroupID, ["pads", destinationID], 1); // Initialize the new pad (will update the listAllPads cache) - padManager.getPad(destinationID, null, callback) + setTimeout(function(){ + padManager.getPad(destinationID, null, callback) // this runs too early. + },10); } // series ], function(err) From c9b0c6896e2e6a4f20151df00edd3aa07d240e94 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 29 Dec 2014 15:08:30 +0100 Subject: [PATCH 235/263] move pad tests - still need to do copy pad and some other functionality IE force --- tests/backend/specs/api/pad.js | 128 +++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) diff --git a/tests/backend/specs/api/pad.js b/tests/backend/specs/api/pad.js index 700c498f..a20c9233 100644 --- a/tests/backend/specs/api/pad.js +++ b/tests/backend/specs/api/pad.js @@ -61,6 +61,16 @@ describe('Permission', function(){ -> setText(padId) -> getLastEdited(padID) -- Should be when setText was performed -> padUsers(padID) -- Should be when setText was performed + + -> setText(padId, "hello world") + -> getLastEdited(padID) -- Should be when pad was made + -> getText(padId) -- Should be "hello world" + -> movePad(padID, newPadId) -- Should provide consistant pad data + -> getText(newPadId) -- Should be "hello world" + -> movePad(newPadID, originalPadId) -- Should provide consistant pad data + -> getText(originalPadId) -- Should be "hello world" + -> getLastEdited(padID) -- Should not be 0 + */ describe('deletePad', function(){ @@ -265,7 +275,125 @@ describe('padUsers', function(){ }); }) +describe('deletePad', function(){ + it('deletes a Pad', function(done) { + api.get(endPoint('deletePad')+"&padID="+testPadId) + .expect(function(res){ + if(res.body.code !== 0) throw new Error("Pad Deletion failed") + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) +var originalPadId = testPadId; +var newPadId = makeid(); + +describe('createPad', function(){ + it('creates a new Pad with text', function(done) { + api.get(endPoint('createPad')+"&padID="+testPadId) + .expect(function(res){ + if(res.body.code !== 0) throw new Error("Pad Creation failed") + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('setText', function(){ + it('Sets text on a pad Id', function(done) { + api.get(endPoint('setText')+"&padID="+testPadId+"&text=hello world") + .expect(function(res){ + if(res.body.code !== 0) throw new Error("Pad Set Text failed") + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('getText', function(){ + it('Gets text on a pad Id', function(done) { + api.get(endPoint('getText')+"&padID="+testPadId) + .expect(function(res){ + if(res.body.code !== 0) throw new Error("Pad Get Text failed") + if(res.body.data.text !== "hello world\n") throw new Error("Pad Text not set properly"); + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('getLastEdited', function(){ + it('Gets when pad was last edited', function(done) { + api.get(endPoint('getLastEdited')+"&padID="+testPadId) + .expect(function(res){ + if(res.body.lastEdited === 0) throw new Error("Get Last Edited Failed") + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('movePad', function(){ + it('Move a Pad to a different Pad ID', function(done) { + api.get(endPoint('movePad')+"&sourceID="+testPadId+"&destinationID="+newPadId+"&force=true") + .expect(function(res){ + console.log(res.body); + if(res.body.code !== 0) throw new Error("Moving Pad Failed") + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('getText', function(){ + it('Gets text on a pad Id', function(done) { + api.get(endPoint('getText')+"&padID="+newPadId) + .expect(function(res){ + if(res.body.data.text !== "hello world\n") throw new Error("Pad Get Text failed") + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('movePad', function(){ + it('Move a Pad to a different Pad ID', function(done) { + api.get(endPoint('movePad')+"&sourceID="+newPadId+"&destinationID="+testPadId+"&force=false") + .expect(function(res){ + if(res.body.code !== 0) throw new Error("Moving Pad Failed") + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('getText', function(){ + it('Gets text on a pad Id', function(done) { + api.get(endPoint('getText')+"&padID="+testPadId) + .expect(function(res){ + if(res.body.data.text !== "hello world\n") throw new Error("Pad Get Text failed") + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('getLastEdited', function(){ + it('Gets when pad was last edited', function(done) { + api.get(endPoint('getLastEdited')+"&padID="+testPadId) + .expect(function(res){ + if(res.body.lastEdited === 0) throw new Error("Get Last Edited Failed") + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +/* + -> movePadForce Test + +*/ var endPoint = function(point){ return '/api/'+apiVersion+'/'+point+'?apikey='+apiKey; From e1c683be3f47a350e6bac3146507bd2d7d7478f6 Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Mon, 29 Dec 2014 16:12:07 +0100 Subject: [PATCH 236/263] differentiate between indents and bullets in unordered lists --- src/static/js/contentcollector.js | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index a378d2c2..b33b1e6e 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -518,9 +518,24 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class } if (tname == "ul" || tname == "ol") { - var type; + var type = node.attribs.class; var rr = cls && /(?:^| )list-([a-z]+[12345678])\b/.exec(cls); - type = rr && rr[1] || (tname == "ul" ? "bullet" : "number") + String(Math.min(_MAX_LIST_LEVEL, (state.listNesting || 0) + 1)); + // lists do not need to have a type, so before we make a wrong guess, check if we find a better hint within the node's children + if(!rr && !type){ + for (var i in node.children){ + if(node.children[i] && node.children[i].name=='ul'){ + type = node.children[i].attribs.class + if(type){ + break + } + } + } + } + if(rr && rr[1]){ + type = rr[1] + } else { + type = (tname == "ul" ? (type.match("indent") || node.attribs.class && node.attribs.class.match("indent") ? "indent" : "bullet") : "number") + 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/)) From a36d6f36a07abb69a8e51b9cfe66d7ea91622e51 Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Mon, 29 Dec 2014 16:12:46 +0100 Subject: [PATCH 237/263] add testcase for import of intended lists --- tests/frontend/specs/importindents.js | 111 ++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 tests/frontend/specs/importindents.js diff --git a/tests/frontend/specs/importindents.js b/tests/frontend/specs/importindents.js new file mode 100644 index 00000000..db2b33b0 --- /dev/null +++ b/tests/frontend/specs/importindents.js @@ -0,0 +1,111 @@ +describe("import indents functionality", function(){ + beforeEach(function(cb){ + helper.newPad(cb); // creates a new pad + this.timeout(60000); + }); + + function getinnertext(){ + var inner = helper.padInner$ + var newtext = "" + inner("div").each(function(line,el){ + newtext += el.innerHTML+"\n" + }) + return newtext + } + function importrequest(data,importurl,type){ + var success; + var error; + var result = $.ajax({ + url: importurl, + type: "post", + processData: false, + async: false, + contentType: 'multipart/form-data; boundary=boundary', + accepts: { + text: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" + }, + data: 'Content-Type: multipart/form-data; boundary=--boundary\r\n\r\n--boundary\r\nContent-Disposition: form-data; name="file"; filename="import.'+type+'"\r\nContent-Type: text/plain\r\n\r\n' + data + '\r\n\r\n--boundary', + error: function(res){ + error = res + } + }) + expect(error).to.be(undefined) + return result + } + function exportfunc(link){ + var exportresults = [] + $.ajaxSetup({ + async:false + }) + $.get(link+"/export/html",function(data){ + var start = data.indexOf("") + var end = data.indexOf("") + var html = data.substr(start+6,end-start-6) + exportresults.push(["html",html]) + }) + $.get(link+"/export/txt",function(data){ + exportresults.push(["txt",data]) + }) + return exportresults + } + + it("import a pad with indents from html", function(done){ + var importurl = helper.padChrome$.window.location.href+'/import' + var htmlWithIndents = '
  • indent line 1
  • indent line 2
    • indent2 line 1
    • indent2 line 2
' + importrequest(htmlWithIndents,importurl,"html") + helper.waitFor(function(){ + return expect(getinnertext()).to.be('\ +
  • indent line 1
\n\ +
  • indent line 2
\n\ +
  • indent2 line 1
\n\ +
  • indent2 line 2
\n\ +
\n') + }) + var results = exportfunc(helper.padChrome$.window.location.href) + expect(results[0][1]).to.be('
  • indent line 1
  • indent line 2
    • indent2 line 1
    • indent2 line 2

') + expect(results[1][1]).to.be('\tindent line 1\n\tindent line 2\n\t\tindent2 line 1\n\t\tindent2 line 2\n\n') + done() + }) + + it("import a pad with indented lists and newlines from html", function(done){ + var importurl = helper.padChrome$.window.location.href+'/import' + var htmlWithIndents = '
  • indent line 1

  • indent 1 line 2
    • indent 2 times line 1

    • indent 2 times line 2
' + importrequest(htmlWithIndents,importurl,"html") + helper.waitFor(function(){ + return expect(getinnertext()).to.be('\ +
  • indent line 1
\n\ +
\n\ +
  • indent 1 line 2
\n\ +
  • indent 2 times line 1
\n\ +
\n\ +
  • indent 2 times line 2
\n\ +
\n') + }) + var results = exportfunc(helper.padChrome$.window.location.href) + expect(results[0][1]).to.be('
  • indent line 1

  • indent 1 line 2
    • indent 2 times line 1

    • indent 2 times line 2

') + expect(results[1][1]).to.be('\tindent line 1\n\n\tindent 1 line 2\n\t\tindent 2 times line 1\n\n\t\tindent 2 times line 2\n\n') + done() + }) + it("import a pad with 8 levels of indents and newlines and attributes from html", function(done){ + var importurl = helper.padChrome$.window.location.href+'/import' + var htmlWithIndents = '
  • indent line 1

  • indent line 2
    • indent2 line 1

        • indent4 line 2 bisu
        • indent4 line 2 bs
        • indent4 line 2 uuis
                • foo
                • foobar bs
          • foobar
    ' + importrequest(htmlWithIndents,importurl,"html") + helper.waitFor(function(){ + return expect(getinnertext()).to.be('\ +
    • indent line 1
    \n\
    \n\ +
    • indent line 2
    \n\ +
    • indent2 line 1
    \n
    \n\ +
    • indent4 line 2 bisu
    \n\ +
    • indent4 line 2 bs
    \n\ +
    • indent4 line 2 uuis
    \n\ +
    • foo
    \n\ +
    • foobar bs
    \n\ +
    • foobar
    \n\ +
    \n') + }) + var results = exportfunc(helper.padChrome$.window.location.href) + expect(results[0][1]).to.be('
    • indent line 1

    • indent line 2
      • indent2 line 1

          • indent4 line 2 bisu
          • indent4 line 2 bs
          • indent4 line 2 uuis
                  • foo
                  • foobar bs
            • foobar

    ') + expect(results[1][1]).to.be('\tindent line 1\n\n\tindent line 2\n\t\tindent2 line 1\n\n\t\t\t\tindent4 line 2 bisu\n\t\t\t\tindent4 line 2 bs\n\t\t\t\tindent4 line 2 uuis\n\t\t\t\t\t\t\t\tfoo\n\t\t\t\t\t\t\t\tfoobar bs\n\t\t\t\t\tfoobar\n\n') + done() + }) +}) From 8e280f46c1d96adf5b32202dd455984c041a8a56 Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Mon, 29 Dec 2014 16:25:41 +0100 Subject: [PATCH 238/263] fix bullet test case, remove indent-testcases because they are in separate file --- tests/frontend/specs/importexport.js | 63 +++++++++++----------------- 1 file changed, 24 insertions(+), 39 deletions(-) diff --git a/tests/frontend/specs/importexport.js b/tests/frontend/specs/importexport.js index 5c642ae3..4ba8d57b 100644 --- a/tests/frontend/specs/importexport.js +++ b/tests/frontend/specs/importexport.js @@ -140,10 +140,29 @@ describe("import functionality", function(){ expect(results[1][1]).to.be('\t* bullet line 1\n\n\t* bullet line 2\n\t\t* bullet2 line 1\n\n\t\t\t\t* bullet4 line 2 bisu\n\t\t\t\t* bullet4 line 2 bs\n\t\t\t\t* bullet4 line 2 uuis\n\n') done() }) - - - //RESULT IS WRONG AT THE LAST LINE - xit("import a pad with 8 levels of bullets and newlines and attributes from html", function(done){ + it("import a pad with nested bullets from html", function(done){ + var importurl = helper.padChrome$.window.location.href+'/import' + var htmlWithBullets = '
    • bullet line 1
    • bullet line 2
      • bullet2 line 1
          • bullet4 line 2
          • bullet4 line 2
          • bullet4 line 2
        • bullet3 line 1
    • bullet2 line 1
    ' + importrequest(htmlWithBullets,importurl,"html") + var oldtext=getinnertext() + helper.waitFor(function(){ + return oldtext != getinnertext() +// return expect(getinnertext()).to.be('\ +//
    • bullet line 1
    \n\ +//
    • bullet line 2
    \n\ +//
    • bullet2 line 1
    \n\ +//
    • bullet4 line 2
    \n\ +//
    • bullet4 line 2
    \n\ +//
    • bullet4 line 2
    \n\ +//
    \n') + }) + + var results = exportfunc(helper.padChrome$.window.location.href) + expect(results[0][1]).to.be('
    • bullet line 1
    • bullet line 2
      • bullet2 line 1
          • bullet4 line 2
          • bullet4 line 2
          • bullet4 line 2
        • bullet3 line 1
    • bullet2 line 1

    ') + expect(results[1][1]).to.be('\t* bullet line 1\n\t* bullet line 2\n\t\t* bullet2 line 1\n\t\t\t\t* bullet4 line 2\n\t\t\t\t* bullet4 line 2\n\t\t\t\t* bullet4 line 2\n\t\t\t* bullet3 line 1\n\t* bullet2 line 1\n\n') + done() + }) + it("import a pad with 8 levels of bullets and newlines and attributes from html", function(done){ var importurl = helper.padChrome$.window.location.href+'/import' var htmlWithBullets = '
    • bullet line 1

    • bullet line 2
      • bullet2 line 1

          • bullet4 line 2 bisu
          • bullet4 line 2 bs
          • bullet4 line 2 uuis
                  • foo
                  • foobar bs
            • foobar
      ' importrequest(htmlWithBullets,importurl,"html") @@ -161,45 +180,11 @@ describe("import functionality", function(){
      \n') }) var results = exportfunc(helper.padChrome$.window.location.href) - expect(results[0][1]).to.be('
      • bullet line 1

      • bullet line 2
        • bullet2 line 1

            • bullet4 line 2 bisu
            • bullet4 line 2 bs
            • bullet4 line 2 uuis
                    • foo
                    • foobar bs
                    • foobar

      ') + expect(results[0][1]).to.be('
      • bullet line 1

      • bullet line 2
        • bullet2 line 1

            • bullet4 line 2 bisu
            • bullet4 line 2 bs
            • bullet4 line 2 uuis
                    • foo
                    • foobar bs
              • foobar

      ') expect(results[1][1]).to.be('\t* bullet line 1\n\n\t* bullet line 2\n\t\t* bullet2 line 1\n\n\t\t\t\t* bullet4 line 2 bisu\n\t\t\t\t* bullet4 line 2 bs\n\t\t\t\t* bullet4 line 2 uuis\n\t\t\t\t\t\t\t\t* foo\n\t\t\t\t\t\t\t\t* foobar bs\n\t\t\t\t\t* foobar\n\n') done() }) - xit("import a pad with indents from html", function(done){ - var importurl = helper.padChrome$.window.location.href+'/import' - var htmlWithIndents = '
      • indent line 1
      • indent line 2
        • indent2 line 1
        • indent2 line 2
      ' - importrequest(htmlWithIndents,importurl,"html") - console.error(getinnertext()) - expect(getinnertext()).to.be('\ -
      • indent line 1
      \n\ -
      • indent line 2
      \n\ -
      • indent2 line 1
      \n\ -
      • indent2 line 2
      \n\ -
      \n') - var results = exportfunc(helper.padChrome$.window.location.href) - console.error(results[0][1]) - expect(results[0][1]).to.be('
      • indent line 1
      • indent line 2
        • indent2 line 1
        • indent2 line 2
      ') - expect(results[1][1]).to.be('') - done() - }) - xit("import a pad with indented lists and newlines from html", function(done){ - var importurl = helper.padChrome$.window.location.href+'/import' - var htmlWithIndents = '
      • indent line 1

      • indent 10 line 2
        • indent 2 times line 1

        • indent 2 times line 2
      ' - importrequest(htmlWithIndents,importurl,"html") - expect(getinnertext()).to.be('\ -
      • indent line 1
      \n\ -
      \n\ -
      • indent 10 line 2
      \n\ -
      • indent 2 times line 1
      \n\ -
      \n\ -
      • indent 2 times line 2
      \n\ -
      \n') - var results = exportfunc(helper.padChrome$.window.location.href) - expect(results[0][1]).to.be('') - expect(results[1][1]).to.be('') - done() - }) xit("import a pad with ordered lists from html", function(done){ var importurl = helper.padChrome$.window.location.href+'/import' var htmlWithBullets = '
      1. number 1 line 1
      1. number 2 line 2
      ' From 6e4e034e425523b9f0e2bf22674d8d36082fcbbd Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Mon, 29 Dec 2014 16:27:40 +0100 Subject: [PATCH 239/263] fix closing of lists --- src/node/utils/ExportHtml.js | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/node/utils/ExportHtml.js b/src/node/utils/ExportHtml.js index bb29b34d..a94c4d97 100644 --- a/src/node/utils/ExportHtml.js +++ b/src/node/utils/ExportHtml.js @@ -305,10 +305,12 @@ function getHTMLFromAtext(pad, atext, authorColors) // want to deal gracefully with blank lines. // => keeps track of the parents level of indentation var lists = []; // e.g. [[1,'bullet'], [3,'bullet'], ...] + var listLevels = [] for (var i = 0; i < textLines.length; i++) { var line = _analyzeLine(textLines[i], attribLines[i], apool); var lineContent = getLineHTML(line.text, line.aline); + listLevels.push(line.listLevel) if (line.listLevel)//If we are inside a list { @@ -379,19 +381,23 @@ function getHTMLFromAtext(pad, atext, authorColors) }*/ else//means we are getting closer to the lowest level of indentation or are at the same level { - while (whichList < lists.length - 1) - { + var toClose = lists.length > 0 ? listLevels[listLevels.length - 2] - line.listLevel : 0 + if( toClose > 0){ + pieces.push('') if(lists[lists.length - 1][1] == "number") { - pieces.push(''); + pieces.push(new Array(toClose+1).join('')) + pieces.push('
    • ', lineContent || '
      '); } else { - pieces.push('
    '); + pieces.push(new Array(toClose+1).join('
')) + pieces.push('
  • ', lineContent || '
    '); } - lists.length--; + lists = lists.slice(0,whichList+1) + } else { + pieces.push('
  • ', lineContent || '
    '); } - pieces.push('
  • ', lineContent || '
    '); } } else//outside any list, need to close line.listLevel of lists @@ -399,10 +405,10 @@ function getHTMLFromAtext(pad, atext, authorColors) if(lists.length > 0){ if(lists[lists.length - 1][1] == "number"){ pieces.push('
  • '); - pieces.push(new Array(lists[lists.length - 1][0]).join('')) + pieces.push(new Array(listLevels[listLevels.length - 2]).join('')) } else { pieces.push(''); - pieces.push(new Array(lists[lists.length - 1][0]).join('')) + pieces.push(new Array(listLevels[listLevels.length - 2]).join('')) } } lists = [] From 3773b6346b919995c311356b7cd582517b6f09ef Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 29 Dec 2014 20:57:58 +0100 Subject: [PATCH 240/263] semi working requires browser refresh --- src/locales/en.json | 1 + src/node/handler/ExportHandler.js | 14 +++- src/node/handler/ImportHandler.js | 111 ++++++++++++++++--------- src/node/hooks/express/importexport.js | 2 +- src/static/css/pad.css | 3 + src/static/js/pad_impexp.js | 1 + src/templates/pad.html | 1 + 7 files changed, 91 insertions(+), 42 deletions(-) diff --git a/src/locales/en.json b/src/locales/en.json index 9a5fe45f..5e074e38 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -44,6 +44,7 @@ "pad.importExport.import": "Upload any text file or document", "pad.importExport.importSuccessful": "Successful!", "pad.importExport.export": "Export current pad as:", + "pad.importExport.exportetherpad": "Etherpad", "pad.importExport.exporthtml": "HTML", "pad.importExport.exportplain": "Plain text", "pad.importExport.exportword": "Microsoft Word", diff --git a/src/node/handler/ExportHandler.js b/src/node/handler/ExportHandler.js index f12d66c2..23d1cb10 100644 --- a/src/node/handler/ExportHandler.js +++ b/src/node/handler/ExportHandler.js @@ -21,6 +21,7 @@ var ERR = require("async-stacktrace"); var exporthtml = require("../utils/ExportHtml"); var exporttxt = require("../utils/ExportTxt"); +var exportEtherpad = require("../utils/ExportEtherpad"); var async = require("async"); var fs = require("fs"); var settings = require('../utils/Settings'); @@ -52,14 +53,21 @@ exports.doExport = function(req, res, padId, type) // if fileName is set then set it to the padId, note that fileName is returned as an array. if(hookFileName.length) fileName = hookFileName; - //tell the browser that this is a downloadable file res.attachment(fileName + "." + type); //if this is a plain text export, we can do this directly // We have to over engineer this because tabs are stored as attributes and not plain text - - if(type == "txt") + if(type == "etherpad"){ + console.log("Exporting Etherpad"); + exportEtherpad.getPadRaw(padId, function(err, pad){ + if(!err){ + res.send(pad); + // return; + } + }); + } + else if(type == "txt") { var txt; var randNum; diff --git a/src/node/handler/ImportHandler.js b/src/node/handler/ImportHandler.js index 55915d76..7ea10988 100644 --- a/src/node/handler/ImportHandler.js +++ b/src/node/handler/ImportHandler.js @@ -29,6 +29,7 @@ var ERR = require("async-stacktrace") , formidable = require('formidable') , os = require("os") , importHtml = require("../utils/ImportHtml") + , importEtherpad = require("../utils/ImportEtherpad") , log4js = require("log4js") , hooks = require("ep_etherpad-lite/static/js/pluginfw/hooks.js"); @@ -53,7 +54,8 @@ exports.doImport = function(req, res, padId) var srcFile, destFile , pad , text - , importHandledByPlugin; + , importHandledByPlugin + , directDatabaseAccess; var randNum = Math.floor(Math.random()*0xFFFFFFFF); @@ -83,7 +85,7 @@ exports.doImport = function(req, res, padId) //this allows us to accept source code files like .c or .java function(callback) { var fileEnding = path.extname(srcFile).toLowerCase() - , knownFileEndings = [".txt", ".doc", ".docx", ".pdf", ".odt", ".html", ".htm"] + , knownFileEndings = [".txt", ".doc", ".docx", ".pdf", ".odt", ".html", ".htm", ".etherpad"] , fileEndingKnown = (knownFileEndings.indexOf(fileEnding) > -1); //if the file ending is known, continue as normal @@ -116,9 +118,22 @@ exports.doImport = function(req, res, padId) } }); }, + function(callback) { + var fileEnding = path.extname(srcFile).toLowerCase() + var fileIsEtherpad = (fileEnding === ".etherpad"); + if(fileIsEtherpad){ + fs.readFile(srcFile, "utf8", function(err, _text){ + directDatabaseAccess = true; + importEtherpad.setPadRaw(padId, _text, function(err){ + console.log("returning"); + return callback(null); + }); + }); + } + }, //convert file to html function(callback) { - if(!importHandledByPlugin){ + if(!importHandledByPlugin || !directDatabaseAccess){ var fileEnding = path.extname(srcFile).toLowerCase(); var fileIsHTML = (fileEnding === ".html" || fileEnding === ".htm"); if (abiword && !fileIsHTML) { @@ -141,7 +156,7 @@ exports.doImport = function(req, res, padId) }, function(callback) { - if (!abiword) { + if (!abiword || !directDatabaseAccess) { // Read the file with no encoding for raw buffer access. fs.readFile(destFile, function(err, buf) { if (err) throw err; @@ -175,50 +190,70 @@ exports.doImport = function(req, res, padId) //read the text function(callback) { - fs.readFile(destFile, "utf8", function(err, _text){ - if(ERR(err, callback)) return; - text = _text; - // Title needs to be stripped out else it appends it to the pad.. - text = text.replace("", "<!-- <title>"); - text = text.replace("","-->"); - - //node on windows has a delay on releasing of the file lock. - //We add a 100ms delay to work around this - if(os.type().indexOf("Windows") > -1){ - setTimeout(function() {callback();}, 100); - } else { - callback(); - } - }); + if(!directDatabaseAccess){ + fs.readFile(destFile, "utf8", function(err, _text){ + if(ERR(err, callback)) return; + text = _text; + // Title needs to be stripped out else it appends it to the pad.. + text = text.replace("", "<!-- <title>"); + text = text.replace("","-->"); + + //node on windows has a delay on releasing of the file lock. + //We add a 100ms delay to work around this + if(os.type().indexOf("Windows") > -1){ + setTimeout(function() {callback();}, 100); + } else { + callback(); + } + }); + }else{ + callback(); + } }, //change text of the pad and broadcast the changeset function(callback) { - var fileEnding = path.extname(srcFile).toLowerCase(); - if (abiword || fileEnding == ".htm" || fileEnding == ".html") { - try{ - importHtml.setPadHTML(pad, text); - }catch(e){ - apiLogger.warn("Error importing, possibly caused by malformed HTML"); + if(!directDatabaseAccess){ + var fileEnding = path.extname(srcFile).toLowerCase(); + if (abiword || fileEnding == ".htm" || fileEnding == ".html") { + try{ + importHtml.setPadHTML(pad, text); + }catch(e){ + apiLogger.warn("Error importing, possibly caused by malformed HTML"); + } + } else { + pad.setText(text); } - } else { - pad.setText(text); } - padMessageHandler.updatePadClients(pad, callback); + + // Load the Pad into memory then brodcast updates to all clients + padManager.unloadPad(padId); + padManager.getPad(padId, function(err, _pad){ + var pad = _pad; + padManager.unloadPad(padId); + padMessageHandler.updatePadClients(pad, function(){ + callback(); + }); + }); + }, //clean up temporary files function(callback) { - //for node < 0.7 compatible - var fileExists = fs.exists || path.exists; - async.parallel([ - function(callback){ - fileExists (srcFile, function(exist) { (exist)? fs.unlink(srcFile, callback): callback(); }); - }, - function(callback){ - fileExists (destFile, function(exist) { (exist)? fs.unlink(destFile, callback): callback(); }); - } - ], callback); + if(!directDatabaseAccess){ + //for node < 0.7 compatible + var fileExists = fs.exists || path.exists; + async.parallel([ + function(callback){ + fileExists (srcFile, function(exist) { (exist)? fs.unlink(srcFile, callback): callback(); }); + }, + function(callback){ + fileExists (destFile, function(exist) { (exist)? fs.unlink(destFile, callback): callback(); }); + } + ], callback); + }else{ + callback(); + } } ], function(err) { diff --git a/src/node/hooks/express/importexport.js b/src/node/hooks/express/importexport.js index 378e8865..f3f05163 100644 --- a/src/node/hooks/express/importexport.js +++ b/src/node/hooks/express/importexport.js @@ -5,7 +5,7 @@ var importHandler = require('../../handler/ImportHandler'); exports.expressCreateServer = function (hook_name, args, cb) { args.app.get('/p/:pad/:rev?/export/:type', function(req, res, next) { - var types = ["pdf", "doc", "txt", "html", "odt"]; + var types = ["pdf", "doc", "txt", "html", "odt", "etherpad"]; //send a 404 if we don't support this filetype if (types.indexOf(req.params.type) == -1) { next(); diff --git a/src/static/css/pad.css b/src/static/css/pad.css index c1035e8d..0ff613fc 100644 --- a/src/static/css/pad.css +++ b/src/static/css/pad.css @@ -689,6 +689,9 @@ table#otheruserstable { #exportpdfa:before { content: "\e803"; } +#exportetherpada:before { + content: "\e806"; +} #exportopena:before { content: "\e805"; } diff --git a/src/static/js/pad_impexp.js b/src/static/js/pad_impexp.js index 20cae2a0..2548b8bb 100644 --- a/src/static/js/pad_impexp.js +++ b/src/static/js/pad_impexp.js @@ -198,6 +198,7 @@ var padimpexp = (function() // build the export links $("#exporthtmla").attr("href", pad_root_path + "/export/html"); + $("#exportetherpada").attr("href", pad_root_path + "/export/etherpad"); $("#exportplaina").attr("href", pad_root_path + "/export/txt"); // activate action to import in the form diff --git a/src/templates/pad.html b/src/templates/pad.html index 2dd66aa9..fa28b542 100644 --- a/src/templates/pad.html +++ b/src/templates/pad.html @@ -203,6 +203,7 @@

    <% e.begin_block("exportColumn"); %> +
    From 79803813696ac609dedb8e98fee9c573343b17d5 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 29 Dec 2014 21:13:07 +0100 Subject: [PATCH 241/263] better test coverage for longer pad data --- tests/backend/specs/api/pad.js | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/tests/backend/specs/api/pad.js b/tests/backend/specs/api/pad.js index a20c9233..4b2e444c 100644 --- a/tests/backend/specs/api/pad.js +++ b/tests/backend/specs/api/pad.js @@ -11,6 +11,7 @@ apiKey = apiKey.replace(/\n$/, ""); var apiVersion = 1; var testPadId = makeid(); var lastEdited = ""; +var text = generateLongText(); describe('Connectivity', function(){ it('errors if can not connect', function(done) { @@ -302,7 +303,7 @@ describe('createPad', function(){ describe('setText', function(){ it('Sets text on a pad Id', function(done) { - api.get(endPoint('setText')+"&padID="+testPadId+"&text=hello world") + api.get(endPoint('setText')+"&padID="+testPadId+"&text="+text) .expect(function(res){ if(res.body.code !== 0) throw new Error("Pad Set Text failed") }) @@ -316,7 +317,7 @@ describe('getText', function(){ api.get(endPoint('getText')+"&padID="+testPadId) .expect(function(res){ if(res.body.code !== 0) throw new Error("Pad Get Text failed") - if(res.body.data.text !== "hello world\n") throw new Error("Pad Text not set properly"); + if(res.body.data.text !== text+"\n") throw new Error("Pad Text not set properly"); }) .expect('Content-Type', /json/) .expect(200, done) @@ -338,7 +339,6 @@ describe('movePad', function(){ it('Move a Pad to a different Pad ID', function(done) { api.get(endPoint('movePad')+"&sourceID="+testPadId+"&destinationID="+newPadId+"&force=true") .expect(function(res){ - console.log(res.body); if(res.body.code !== 0) throw new Error("Moving Pad Failed") }) .expect('Content-Type', /json/) @@ -350,7 +350,8 @@ describe('getText', function(){ it('Gets text on a pad Id', function(done) { api.get(endPoint('getText')+"&padID="+newPadId) .expect(function(res){ - if(res.body.data.text !== "hello world\n") throw new Error("Pad Get Text failed") + console.log(res.body.data.text); + if(res.body.data.text !== text+"\n") throw new Error("Pad Get Text failed") }) .expect('Content-Type', /json/) .expect(200, done) @@ -372,7 +373,7 @@ describe('getText', function(){ it('Gets text on a pad Id', function(done) { api.get(endPoint('getText')+"&padID="+testPadId) .expect(function(res){ - if(res.body.data.text !== "hello world\n") throw new Error("Pad Get Text failed") + if(res.body.data.text !== text+"\n") throw new Error("Pad Get Text failed") }) .expect('Content-Type', /json/) .expect(200, done) @@ -409,3 +410,13 @@ function makeid() } return text; } + +function generateLongText(){ + var text = ""; + var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + + for( var i=0; i < 80000; i++ ){ + text += possible.charAt(Math.floor(Math.random() * possible.length)); + } + return text; +} From 1081156f134a5c71bc38ab4a3687a900cb35ba14 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 29 Dec 2014 21:13:49 +0100 Subject: [PATCH 242/263] whoopsi, required files --- src/node/utils/ExportEtherpad.js | 44 ++++++++++++++++++++++++++++++++ src/node/utils/ImportEtherpad.js | 39 ++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 src/node/utils/ExportEtherpad.js create mode 100644 src/node/utils/ImportEtherpad.js diff --git a/src/node/utils/ExportEtherpad.js b/src/node/utils/ExportEtherpad.js new file mode 100644 index 00000000..99d86140 --- /dev/null +++ b/src/node/utils/ExportEtherpad.js @@ -0,0 +1,44 @@ +/** + * Copyright 2014 John McLear. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS-IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +var async = require("async"); +var db = require("../db/DB").db; +var ERR = require("async-stacktrace"); + +exports.getPadRaw = function(padId, callback){ + async.waterfall([ + function(cb){ + db.findKeys("pad:"+padId+"*", null, function(err,records){ + if(!err){ + cb(err, records); + } + }) + }, + function(records, cb){ + var data = {}; + async.forEachSeries(Object.keys(records), function(key, r){ + db.get(records[key], function(err, entry){ + data[records[key]] = entry; + r(null); // callback; + }); + }, function(err){ + cb(err, data); + }) + }], function(err, data){ + callback(null, data); + }); +} diff --git a/src/node/utils/ImportEtherpad.js b/src/node/utils/ImportEtherpad.js new file mode 100644 index 00000000..b18708ff --- /dev/null +++ b/src/node/utils/ImportEtherpad.js @@ -0,0 +1,39 @@ +/** + * 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 log4js = require('log4js'); +var async = require("async"); +var db = require("../db/DB").db; + +exports.setPadRaw = function(padId, records, callback){ + records = JSON.parse(records); + + async.eachSeries(Object.keys(records), function(key, cb){ + var value = records[key] + + // rewrite padId + var oldPadId = key.split(":"); + oldPadId[1] = padId; + var newKey = oldPadId.join(":"); // create the new key + + // Write the value to the server + db.set(newKey, value); + + cb(); + }, function(){ + callback(null, true); + }); +} From ab5e7381a29ae442ede7dde760f7d69a9228fe80 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 29 Dec 2014 21:35:10 +0100 Subject: [PATCH 243/263] working for all files --- src/node/handler/ImportHandler.js | 44 +++++++++++++++++-------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/node/handler/ImportHandler.js b/src/node/handler/ImportHandler.js index 7ea10988..813a8d19 100644 --- a/src/node/handler/ImportHandler.js +++ b/src/node/handler/ImportHandler.js @@ -125,10 +125,11 @@ exports.doImport = function(req, res, padId) fs.readFile(srcFile, "utf8", function(err, _text){ directDatabaseAccess = true; importEtherpad.setPadRaw(padId, _text, function(err){ - console.log("returning"); - return callback(null); + callback(); }); }); + }else{ + callback(); } }, //convert file to html @@ -156,24 +157,28 @@ exports.doImport = function(req, res, padId) }, function(callback) { - if (!abiword || !directDatabaseAccess) { - // Read the file with no encoding for raw buffer access. - fs.readFile(destFile, function(err, buf) { - if (err) throw err; - var isAscii = true; - // Check if there are only ascii chars in the uploaded file - for (var i=0, len=buf.length; i 240) { - isAscii=false; - break; + if (!abiword){ + if(!directDatabaseAccess) { + // Read the file with no encoding for raw buffer access. + fs.readFile(destFile, function(err, buf) { + if (err) throw err; + var isAscii = true; + // Check if there are only ascii chars in the uploaded file + for (var i=0, len=buf.length; i 240) { + isAscii=false; + break; + } } - } - if (isAscii) { - callback(); - } else { - callback("uploadFailed"); - } - }); + if (isAscii) { + callback(); + } else { + callback("uploadFailed"); + } + }); + }else{ + callback(); + } } else { callback(); } @@ -256,7 +261,6 @@ exports.doImport = function(req, res, padId) } } ], function(err) { - var status = "ok"; //check for known errors and replace the status From a6400b3f619cc62602afa8fc9c6ff1686c8aa089 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 29 Dec 2014 22:02:24 +0100 Subject: [PATCH 244/263] allow only for pads less than 10 to be overwritten --- src/node/handler/ImportHandler.js | 24 ++++++++++++++++++------ src/node/utils/ImportEtherpad.js | 1 - 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/node/handler/ImportHandler.js b/src/node/handler/ImportHandler.js index 813a8d19..af4c01f1 100644 --- a/src/node/handler/ImportHandler.js +++ b/src/node/handler/ImportHandler.js @@ -121,12 +121,24 @@ exports.doImport = function(req, res, padId) function(callback) { var fileEnding = path.extname(srcFile).toLowerCase() var fileIsEtherpad = (fileEnding === ".etherpad"); + if(fileIsEtherpad){ - fs.readFile(srcFile, "utf8", function(err, _text){ - directDatabaseAccess = true; - importEtherpad.setPadRaw(padId, _text, function(err){ - callback(); - }); + // we do this here so we can see if the pad has quit ea few edits + padManager.getPad(padId, function(err, _pad){ + console.error(_pad); + var headCount = _pad.head; + console.error(headCount); + if(headCount >= 10){ + apiLogger.warn("Direct database Import attempt of a pad that already has content, we wont be doing this") + return callback("padHasData"); + }else{ + fs.readFile(srcFile, "utf8", function(err, _text){ + directDatabaseAccess = true; + importEtherpad.setPadRaw(padId, _text, function(err){ + callback(); + }); + }); + } }); }else{ callback(); @@ -264,7 +276,7 @@ exports.doImport = function(req, res, padId) var status = "ok"; //check for known errors and replace the status - if(err == "uploadFailed" || err == "convertFailed") + if(err == "uploadFailed" || err == "convertFailed" || err == "padHasData") { status = err; err = null; diff --git a/src/node/utils/ImportEtherpad.js b/src/node/utils/ImportEtherpad.js index b18708ff..be6a8929 100644 --- a/src/node/utils/ImportEtherpad.js +++ b/src/node/utils/ImportEtherpad.js @@ -23,7 +23,6 @@ exports.setPadRaw = function(padId, records, callback){ async.eachSeries(Object.keys(records), function(key, cb){ var value = records[key] - // rewrite padId var oldPadId = key.split(":"); oldPadId[1] = padId; From a2262c56b987b0ec587f0790fc74e6590bccce71 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 29 Dec 2014 22:05:14 +0100 Subject: [PATCH 245/263] msg for user --- src/locales/en.json | 1 + src/static/js/pad_impexp.js | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/locales/en.json b/src/locales/en.json index 5e074e38..17a83b46 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -130,6 +130,7 @@ "pad.impexp.importing": "Importing...", "pad.impexp.confirmimport": "Importing a file will overwrite the current text of the pad. Are you sure you want to proceed?", "pad.impexp.convertFailed": "We were not able to import this file. Please use a different document format or copy paste manually", + "pad.impexp.padHasData": "We were not able to import this file because this Pad has already had changes, please import to a new pad", "pad.impexp.uploadFailed": "The upload failed, please try again", "pad.impexp.importfailed": "Import failed", "pad.impexp.copypaste": "Please copy paste", diff --git a/src/static/js/pad_impexp.js b/src/static/js/pad_impexp.js index 2548b8bb..d9a8953d 100644 --- a/src/static/js/pad_impexp.js +++ b/src/static/js/pad_impexp.js @@ -109,6 +109,8 @@ var padimpexp = (function() msg = html10n.get("pad.impexp.convertFailed"); } else if(status === "uploadFailed"){ msg = html10n.get("pad.impexp.uploadFailed"); + } else if(status === "padHasData"){ + msg = html10n.get("pad.impexp.padHasData"); } function showError(fade) From ec2b844f9433b61c570fd0624644f19de43e4208 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 29 Dec 2014 22:51:31 +0100 Subject: [PATCH 246/263] authors --- src/node/utils/ExportEtherpad.js | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/node/utils/ExportEtherpad.js b/src/node/utils/ExportEtherpad.js index 99d86140..0bc2bf04 100644 --- a/src/node/utils/ExportEtherpad.js +++ b/src/node/utils/ExportEtherpad.js @@ -22,6 +22,8 @@ var ERR = require("async-stacktrace"); exports.getPadRaw = function(padId, callback){ async.waterfall([ function(cb){ + + // Get the Pad available content keys db.findKeys("pad:"+padId+"*", null, function(err,records){ if(!err){ cb(err, records); @@ -30,15 +32,37 @@ exports.getPadRaw = function(padId, callback){ }, function(records, cb){ var data = {}; + async.forEachSeries(Object.keys(records), function(key, r){ + + // For each piece of info about a pad. db.get(records[key], function(err, entry){ data[records[key]] = entry; + + // Get the Pad Authors + if(entry.pool && entry.pool.numToAttrib){ + var authors = entry.pool.numToAttrib; + async.forEachSeries(Object.keys(authors), function(k, c){ + if(authors[k][0] === "author"){ + var authorId = authors[k][1]; + + // Get the author info + db.get("globalAuthor:"+authorId, function(e, authorEntry){ + if(!e) data["globalAuthor:"+authorId] = authorEntry; + }); + + } + // console.log("authorsK", authors[k]); + c(null); + }); + } r(null); // callback; }); }, function(err){ cb(err, data); }) - }], function(err, data){ + } + ], function(err, data){ callback(null, data); }); } From 1e0de620be85b7b4d8af1472a52a851933a921d0 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 29 Dec 2014 23:08:17 +0100 Subject: [PATCH 247/263] more author logic --- src/node/utils/ImportEtherpad.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/node/utils/ImportEtherpad.js b/src/node/utils/ImportEtherpad.js index be6a8929..f3d3c4b0 100644 --- a/src/node/utils/ImportEtherpad.js +++ b/src/node/utils/ImportEtherpad.js @@ -23,10 +23,19 @@ exports.setPadRaw = function(padId, records, callback){ async.eachSeries(Object.keys(records), function(key, cb){ var value = records[key] + // rewrite padId var oldPadId = key.split(":"); oldPadId[1] = padId; - var newKey = oldPadId.join(":"); // create the new key + if(oldPadId[0] === "pad"){ + var newKey = oldPadId.join(":"); // create the new key + } + + // Add the author to this new pad + if(oldPadId[0] === "globalAuthor"){ + value.padIDs[padId] = 1 ; + var newKey = value; + } // Write the value to the server db.set(newKey, value); From b8648b4a49a5e3d050a3a414963c0877f49781c8 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 29 Dec 2014 23:08:42 +0100 Subject: [PATCH 248/263] remove error logging --- src/node/handler/ImportHandler.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/node/handler/ImportHandler.js b/src/node/handler/ImportHandler.js index af4c01f1..65006fa7 100644 --- a/src/node/handler/ImportHandler.js +++ b/src/node/handler/ImportHandler.js @@ -125,9 +125,7 @@ exports.doImport = function(req, res, padId) if(fileIsEtherpad){ // we do this here so we can see if the pad has quit ea few edits padManager.getPad(padId, function(err, _pad){ - console.error(_pad); var headCount = _pad.head; - console.error(headCount); if(headCount >= 10){ apiLogger.warn("Direct database Import attempt of a pad that already has content, we wont be doing this") return callback("padHasData"); From 0676d2fe24b5fe4bd1b84ffad4c23e2fd4029d75 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 30 Dec 2014 00:01:15 +0100 Subject: [PATCH 249/263] working author import --- src/node/utils/ImportEtherpad.js | 34 ++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/src/node/utils/ImportEtherpad.js b/src/node/utils/ImportEtherpad.js index f3d3c4b0..942f4f18 100644 --- a/src/node/utils/ImportEtherpad.js +++ b/src/node/utils/ImportEtherpad.js @@ -20,26 +20,34 @@ var db = require("../db/DB").db; exports.setPadRaw = function(padId, records, callback){ records = JSON.parse(records); - + async.eachSeries(Object.keys(records), function(key, cb){ var value = records[key] - // rewrite padId - var oldPadId = key.split(":"); - oldPadId[1] = padId; - if(oldPadId[0] === "pad"){ - var newKey = oldPadId.join(":"); // create the new key - } + // we know its an author + if(value.padIDs){ + // rewrite author pad ids + value.padIDs[padId] = 1; + var newKey = key; - // Add the author to this new pad - if(oldPadId[0] === "globalAuthor"){ - value.padIDs[padId] = 1 ; - var newKey = value; - } + }else{ + // we can split it to look to see if its pad data + var oldPadId = key.split(":"); + // we know its pad data.. + if(oldPadId[0] === "pad"){ + + // so set the new pad id for the author + oldPadId[1] = padId; + + // and create the value + var newKey = oldPadId.join(":"); // create the new key + } + + } // Write the value to the server db.set(newKey, value); - + cb(); }, function(){ callback(null, true); From 99a239fa9abf5df4c16aff0259ad4bf32256ad99 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 30 Dec 2014 00:10:08 +0100 Subject: [PATCH 250/263] remove console log --- src/node/handler/ExportHandler.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/node/handler/ExportHandler.js b/src/node/handler/ExportHandler.js index 23d1cb10..39e7f564 100644 --- a/src/node/handler/ExportHandler.js +++ b/src/node/handler/ExportHandler.js @@ -59,7 +59,6 @@ exports.doExport = function(req, res, padId, type) //if this is a plain text export, we can do this directly // We have to over engineer this because tabs are stored as attributes and not plain text if(type == "etherpad"){ - console.log("Exporting Etherpad"); exportEtherpad.getPadRaw(padId, function(err, pad){ if(!err){ res.send(pad); From ac4f9eb4cebc0e8dbaa0df425aae660e3fc22071 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 30 Dec 2014 00:12:26 +0100 Subject: [PATCH 251/263] licensing --- src/node/handler/ImportHandler.js | 1 + src/node/utils/ExportEtherpad.js | 2 +- src/node/utils/ImportEtherpad.js | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/node/handler/ImportHandler.js b/src/node/handler/ImportHandler.js index 65006fa7..14cc1019 100644 --- a/src/node/handler/ImportHandler.js +++ b/src/node/handler/ImportHandler.js @@ -5,6 +5,7 @@ /* * 2011 Peter 'Pita' Martischka (Primary Technology Ltd) * 2012 Iván Eixarch + * 2014 John McLear (Etherpad Foundation / McLear Ltd) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/node/utils/ExportEtherpad.js b/src/node/utils/ExportEtherpad.js index 0bc2bf04..36df452d 100644 --- a/src/node/utils/ExportEtherpad.js +++ b/src/node/utils/ExportEtherpad.js @@ -1,5 +1,5 @@ /** - * Copyright 2014 John McLear. + * 2014 John McLear (Etherpad Foundation / McLear Ltd) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/node/utils/ImportEtherpad.js b/src/node/utils/ImportEtherpad.js index 942f4f18..8daeb536 100644 --- a/src/node/utils/ImportEtherpad.js +++ b/src/node/utils/ImportEtherpad.js @@ -1,5 +1,5 @@ /** - * Copyright Yaco Sistemas S.L. 2011. + * 2014 John McLear (Etherpad Foundation / McLear Ltd) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From 5ba3cab445e4909275b97fc7414fa56c6c94578d Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 30 Dec 2014 00:13:01 +0100 Subject: [PATCH 252/263] better take some responsibility --- src/node/handler/ExportHandler.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/node/handler/ExportHandler.js b/src/node/handler/ExportHandler.js index 39e7f564..0a0e51f1 100644 --- a/src/node/handler/ExportHandler.js +++ b/src/node/handler/ExportHandler.js @@ -4,6 +4,7 @@ /* * 2011 Peter 'Pita' Martischka (Primary Technology Ltd) + * 2014 John McLear (Etherpad Foundation / McLear Ltd) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From a07d1722fc6532d5497bccac63f9196717ebce34 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 30 Dec 2014 12:12:24 +0100 Subject: [PATCH 253/263] no errors on chrome client --- src/node/handler/ImportHandler.js | 15 +++++++++++---- src/static/js/pad_impexp.js | 4 ++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/node/handler/ImportHandler.js b/src/node/handler/ImportHandler.js index 14cc1019..a511637c 100644 --- a/src/node/handler/ImportHandler.js +++ b/src/node/handler/ImportHandler.js @@ -247,9 +247,16 @@ exports.doImport = function(req, res, padId) padManager.getPad(padId, function(err, _pad){ var pad = _pad; padManager.unloadPad(padId); - padMessageHandler.updatePadClients(pad, function(){ + + // direct Database Access means a pad user should perform a switchToPad + // and not attempt to recieve updated pad data.. + if(!directDatabaseAccess){ + padMessageHandler.updatePadClients(pad, function(){ + callback(); + }); + }else{ callback(); - }); + } }); }, @@ -282,7 +289,7 @@ exports.doImport = function(req, res, padId) } ERR(err); - + //close the connection res.send( " \ @@ -293,7 +300,7 @@ exports.doImport = function(req, res, padId) if(navigator.userAgent.indexOf('MSIE') === -1){ \ document.domain = document.domain; \ } \ - var impexp = window.parent.padimpexp.handleFrameCall('" + status + "'); \ + var impexp = window.parent.padimpexp.handleFrameCall('" + directDatabaseAccess +"', '" + status + "'); \ }) \ " , 200); diff --git a/src/static/js/pad_impexp.js b/src/static/js/pad_impexp.js index d9a8953d..77f1eb28 100644 --- a/src/static/js/pad_impexp.js +++ b/src/static/js/pad_impexp.js @@ -237,13 +237,13 @@ var padimpexp = (function() $('#importform').submit(fileInputSubmit); $('.disabledexport').click(cantExport); }, - handleFrameCall: function(status) + handleFrameCall: function(directDatabaseAccess, status) { if (status !== "ok") { importFailed(status); } - + if(directDatabaseAccess) pad.switchToPad(clientVars.padId); importDone(); }, disable: function() From 16713d8b3bf7c3acaba4c97d0b8f4744f66856ed Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 30 Dec 2014 14:43:00 +0100 Subject: [PATCH 254/263] some demo export loic for handling OL incrementation --- src/node/utils/ExportHtml.js | 44 ++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/src/node/utils/ExportHtml.js b/src/node/utils/ExportHtml.js index 85d5e7a2..30a92bd6 100644 --- a/src/node/utils/ExportHtml.js +++ b/src/node/utils/ExportHtml.js @@ -449,22 +449,33 @@ exports.getPadHTMLDocument = function (padId, revNum, noDocType, callback) 'font-size: 13px;\n' + 'line-height: 17px; }' + 'ul.indent { list-style-type: none; }' + - 'ol { list-style-type: decimal; }' + - 'ol ol { list-style-type: lower-latin; }' + - 'ol ol ol { list-style-type: lower-roman; }' + - 'ol ol ol ol { list-style-type: decimal; }' + - 'ol ol ol ol ol { list-style-type: lower-latin; }' + - 'ol ol ol ol ol ol{ list-style-type: lower-roman; }' + - 'ol ol ol ol ol ol ol { list-style-type: decimal; }' + - 'ol ol ol ol ol ol ol ol{ list-style-type: lower-latin; }' + - 'ol ol ol ol ol ol ol ol ol { list-style-type: decimal; }' + - 'ol ol ol ol ol ol ol ol ol ol { list-style-type: lower-latin; }' + - 'ol ol ol ol ol ol ol ol ol ol ol { list-style-type: lower-roman; }' + - 'ol ol ol ol ol ol ol ol ol ol ol ol { list-style-type: decimal; }' + - 'ol ol ol ol ol ol ol ol ol ol ol ol ol { list-style-type: lower-latin; }' + - 'ol ol ol ol ol ol ol ol ol ol ol ol ol ol{ list-style-type: lower-roman; }' + - 'ol ol ol ol ol ol ol ol ol ol ol ol ol ol ol { list-style-type: decimal; }' + - 'ol ol ol ol ol ol ol ol ol ol ol ol ol ol ol ol{ list-style-type: lower-latin; }' + + + 'ol { list-style-type: none; }' + + 'body > ol { counter-reset: first second; }' + + 'ol > li:before {' + + 'content: counter(first) ". " ;'+ + 'counter-increment: first;}' + + + 'ol > li > ol > li:before {' + + 'content: counter(first) "." counter(second) ". " ;'+ + 'counter-increment: second;}' + + + 'ol > li > ol > li > ol > li:before {' + + 'content: counter(first) "." counter(second) "." counter(third) ". ";'+ + 'counter-increment: third;}' + + + 'ol > li > ol > li > ol > li > ol > li:before {' + + 'content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) ". ";'+ + 'counter-increment: fourth;}' + + + 'ol > li > ol > li > ol > li > ol > li > ol > li:before {' + + 'content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) ". ";'+ + 'counter-increment: fifth;}' + + + 'ol > li > ol > li > ol > li > ol > li > ol > li > ol > li:before {' + + 'content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) ". ";'+ + 'counter-increment: sixth;}' + + stylesForExportCSS + '\n' + '\n') + ''; @@ -479,7 +490,6 @@ exports.getPadHTMLDocument = function (padId, revNum, noDocType, callback) }); }; - // copied from ACE var _REGEX_WORDCHAR = /[\u0030-\u0039\u0041-\u005A\u0061-\u007A\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\u1FFF\u3040-\u9FFF\uF900-\uFDFF\uFE70-\uFEFE\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFDC]/; var _REGEX_SPACE = /\s/; From 90e83ed6b5a1676495db6c704a31c1f5b56bc22e Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 30 Dec 2014 14:53:44 +0100 Subject: [PATCH 255/263] more styling --- src/node/utils/ExportHtml.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/node/utils/ExportHtml.js b/src/node/utils/ExportHtml.js index e23efb48..4b2843c2 100644 --- a/src/node/utils/ExportHtml.js +++ b/src/node/utils/ExportHtml.js @@ -469,32 +469,39 @@ exports.getPadHTMLDocument = function (padId, revNum, noDocType, callback) 'line-height: 17px; }' + 'ul.indent { list-style-type: none; }' + - 'ol { list-style-type: none; }' + + 'ol { list-style-type: none; padding-left:0;}' + 'body > ol { counter-reset: first second; }' + 'ol > li:before {' + 'content: counter(first) ". " ;'+ 'counter-increment: first;}' + - 'ol > li > ol > li:before {' + + 'ol > ol > li:before {' + 'content: counter(first) "." counter(second) ". " ;'+ 'counter-increment: second;}' + - 'ol > li > ol > li > ol > li:before {' + + 'ol > ol > ol > li:before {' + 'content: counter(first) "." counter(second) "." counter(third) ". ";'+ 'counter-increment: third;}' + - 'ol > li > ol > li > ol > li > ol > li:before {' + + 'ol > ol > ol > ol > li:before {' + 'content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) ". ";'+ 'counter-increment: fourth;}' + - 'ol > li > ol > li > ol > li > ol > li > ol > li:before {' + + 'ol > ol > ol > ol > ol > li:before {' + 'content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) ". ";'+ 'counter-increment: fifth;}' + - 'ol > li > ol > li > ol > li > ol > li > ol > li > ol > li:before {' + + 'ol > ol > ol > ol > ol > ol > li:before {' + 'content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) ". ";'+ 'counter-increment: sixth;}' + + 'ol{ text-indent: 0px; }' + + 'ol > ol{ text-indent: 10px; }' + + 'ol > ol > ol{ text-indent: 20px; }' + + 'ol > ol > ol > ol{ text-indent: 30px; }' + + 'ol > ol > ol > ol > ol{ text-indent: 40px; }' + + 'ol > ol > ol > ol > ol > ol{ text-indent: 50px; }' + + stylesForExportCSS + '\n' + '\n') + ''; From cfe75c7f3f951ebae00255dd3799944c0a92ce68 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 30 Dec 2014 17:45:26 +0100 Subject: [PATCH 256/263] Clean-up after removing list attribute: Remove list numbering attribute --- src/static/js/AttributeManager.js | 4 ++-- src/static/js/ace2_inner.js | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/static/js/AttributeManager.js b/src/static/js/AttributeManager.js index 1da1056a..a11f6cef 100644 --- a/src/static/js/AttributeManager.js +++ b/src/static/js/AttributeManager.js @@ -169,11 +169,11 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ if(attrib[0] === attributeName) return [attributeName, null] return attrib }) - + if(hasMarker){ ChangesetUtils.buildKeepRange(this.rep, builder, loc, (loc = [lineNum, 0])); // If length == 4, there's [author, lmkr, insertorder, + the attrib being removed] thus we can remove the marker entirely - if(attribs.length == 4) ChangesetUtils.buildRemoveRange(this.rep, builder, loc, (loc = [lineNum, 1])) + if(attribs.length <= 4) ChangesetUtils.buildRemoveRange(this.rep, builder, loc, (loc = [lineNum, 1])) else ChangesetUtils.buildKeepRange(this.rep, builder, loc, (loc = [lineNum, 1]), attribs, this.rep.apool); } diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index f1fc1160..86f69bab 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -5073,6 +5073,7 @@ function Ace2Inner(){ { if(listType == ''){ documentAttributeManager.removeAttributeOnLine(lineNum, listAttributeName); + documentAttributeManager.removeAttributeOnLine(lineNum, 'start'); }else{ documentAttributeManager.setAttributeOnLine(lineNum, listAttributeName, listType); } From b0da214ad53fa31e1cb5dca3dd75d90ac386492f Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Tue, 30 Dec 2014 18:06:41 +0100 Subject: [PATCH 257/263] hack to avoid warnings in swagger usage --- src/node/hooks/express/swagger.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/node/hooks/express/swagger.js b/src/node/hooks/express/swagger.js index 55706a70..b9308dee 100644 --- a/src/node/hooks/express/swagger.js +++ b/src/node/hooks/express/swagger.js @@ -355,7 +355,17 @@ exports.expressCreateServer = function (hook_name, args, cb) { args.app.use(basePath, subpath); - swagger.setAppHandler(subpath); + //hack! + var swagger_temp = swagger + swagger = swagger.createNew(subpath); + swagger.params = swagger_temp.params + swagger.queryParam = swagger_temp.queryParam + swagger.pathParam = swagger_temp.pathParam + swagger.bodyParam = swagger_temp.bodyParam + swagger.formParam = swagger_temp.formParam + swagger.headerParam = swagger_temp.headerParam + swagger.error = swagger_temp.error + //swagger.setAppHandler(subpath); swagger.addModels(swaggerModels); From 01c667aa2ec22e6c94fedb7580c291fcc73e8f5f Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 31 Dec 2014 00:53:20 +0000 Subject: [PATCH 258/263] export html more styles --- src/node/utils/ExportHtml.js | 50 ++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/node/utils/ExportHtml.js b/src/node/utils/ExportHtml.js index 4b2843c2..c882e0ef 100644 --- a/src/node/utils/ExportHtml.js +++ b/src/node/utils/ExportHtml.js @@ -495,12 +495,62 @@ exports.getPadHTMLDocument = function (padId, revNum, noDocType, callback) 'content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) ". ";'+ 'counter-increment: sixth;}' + + 'ol > ol > ol > ol > ol > ol > ol > li:before {' + + 'content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(seventh) ". ";'+ + 'counter-increment: seventh;}' + + + 'ol > ol > ol > ol > ol > ol > ol > ol > li:before {' + + 'content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(seventh) "." counter(eigth) ". ";'+ + 'counter-increment: eigth;}' + + + 'ol > ol > ol > ol > ol > ol > ol > ol > ol > li:before {' + + 'content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(seventh) "." counter(eight) "." counter(ninth) ". ";'+ + 'counter-increment: ninth;}' + + + 'ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > li:before {' + + 'content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(seventh) "." counter(eighth) "." counter(ninth) "." counter(tenth) ". ";'+ + 'counter-increment: tenth;}' + + + 'ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > li:before {' + + 'content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(seventh) "." counter(eighth) "." counter(ninth) "." counter(tenth) "." counter(eleventh) ". ";'+ + 'counter-increment: eleventh;}' + + + 'ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > li:before {' + + 'content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(seventh) "." counter(eigth) "." counter(ninth) "." counter(tenth) "." counter(eleventh) "." counter(twelth) ". ";'+ + 'counter-increment: twelth;}' + + + 'ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > li:before {' + + 'content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(seventh) "." counter(eigth) "." counter(ninth) "." counter(tenth) "." counter(eleventh) "." counter(twelth) "." counter(thirteenth) ". ";'+ + 'counter-increment: thirteenth;}' + + + 'ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > li:before {' + + 'content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(seventh) "." counter(eigth) "." counter(ninth) "." counter(tenth) "." counter(eleventh) "." counter(twelth) "." counter(thirteenth) "." counter(fourteenth) ". ";'+ + 'counter-increment: fourteenth;}' + + + 'ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > li:before {' + + 'content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(seventh) "." counter(eigth) "." counter(ninth) "." counter(tenth) "." counter(eleventh) "." counter(twelth) "." counter(thirteenth) "." counter(fourteenth) "." counter(fifteenth) ". ";'+ + 'counter-increment: fifteenth;}' + + + 'ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > li:before {' + + 'content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(seventh) "." counter(eigth) "." counter(ninth) "." counter(tenth) "." counter(eleventh) "." counter(twelth) "." counter(thirteenth) "." counter(fourteenth) "." counter(fifteenth) "." counter(sixthteenth) ". ";'+ + 'counter-increment: sixthteenth;}' + + 'ol{ text-indent: 0px; }' + 'ol > ol{ text-indent: 10px; }' + 'ol > ol > ol{ text-indent: 20px; }' + 'ol > ol > ol > ol{ text-indent: 30px; }' + 'ol > ol > ol > ol > ol{ text-indent: 40px; }' + 'ol > ol > ol > ol > ol > ol{ text-indent: 50px; }' + + 'ol > ol > ol > ol > ol > ol > ol{ text-indent: 60px; }' + + 'ol > ol > ol > ol > ol > ol > ol > ol{ text-indent: 70px; }' + + 'ol > ol > ol > ol > ol > ol > ol > ol > ol{ text-indent: 80px; }' + + 'ol > ol > ol > ol > ol > ol > ol > ol > ol > ol{ text-indent: 90px; }' + + 'ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol{ text-indent: 100px; }' + + 'ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol{ text-indent: 110px; }' + + 'ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol { text-indent: 120px; }' + + 'ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol{ text-indent: 130px; }' + + 'ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol{ text-indent: 140px; }' + + 'ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol{ text-indent: 150px; }' + stylesForExportCSS + '\n' + '\n') + From 335bf3dc4a21224430bcd01d5056d563be5f8e0d Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 31 Dec 2014 01:46:53 +0000 Subject: [PATCH 259/263] more line polish --- src/static/css/iframe_editor.css | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/static/css/iframe_editor.css b/src/static/css/iframe_editor.css index 575ee1a6..1f247e59 100644 --- a/src/static/css/iframe_editor.css +++ b/src/static/css/iframe_editor.css @@ -269,32 +269,32 @@ ol.list-number16{ text-indent: 150px; } } .list-number8 li:before { - content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(eighth) ". " ; + content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(seventh) "." counter(eighth) ". " ; counter-increment: eighth 1; } .list-number9 li:before { - content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(eighth) "." counter(ninth) ". "; + content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(seventh) "." counter(eighth) "." counter(ninth) ". "; counter-increment: ninth 1; } .list-number10 li:before { - content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(eighth) "." counter(ninth) "." counter(tenth) ". "; + content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(seventh) "." counter(eighth) "." counter(ninth) "." counter(tenth) ". "; counter-increment: tenth 1; } .list-number11 li:before { - content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(eighth) "." counter(ninth) "." counter(tenth) "." counter(eleventh) ". "; + content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(seventh) "." counter(eighth) "." counter(ninth) "." counter(tenth) "." counter(eleventh) ". "; counter-increment: eleventh 1; } .list-number12 li:before { - content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(eighth) "." counter(ninth) "." counter(tenth) "." counter(eleventh) "." counter(twelth) ". "; + content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(seventh) "." counter(eighth) "." counter(ninth) "." counter(tenth) "." counter(eleventh) "." counter(twelth) ". "; counter-increment: twelth 1; } .list-number13 li:before { - content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(eighth) "." counter(ninth) "." counter(tenth) "." counter(eleventh) "." counter(twelth) "." counter(thirteenth) ". "; + content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(seventh) "." counter(eighth) "." counter(ninth) "." counter(tenth) "." counter(eleventh) "." counter(twelth) "." counter(thirteenth) ". "; counter-increment: thirteenth 1; } From 83f62bb6a92fb35b50a40f2faf325358ac384d81 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 31 Dec 2014 13:21:36 +0000 Subject: [PATCH 260/263] remove console log --- tests/backend/specs/api/pad.js | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/backend/specs/api/pad.js b/tests/backend/specs/api/pad.js index 4b2e444c..52e7c917 100644 --- a/tests/backend/specs/api/pad.js +++ b/tests/backend/specs/api/pad.js @@ -350,7 +350,6 @@ describe('getText', function(){ it('Gets text on a pad Id', function(done) { api.get(endPoint('getText')+"&padID="+newPadId) .expect(function(res){ - console.log(res.body.data.text); if(res.body.data.text !== text+"\n") throw new Error("Pad Get Text failed") }) .expect('Content-Type', /json/) From 8eb723b90618bf186e4073254ed2b88b1c1d29a3 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 31 Dec 2014 14:16:10 +0000 Subject: [PATCH 261/263] patch for e1c683be3f47a350e6bac3146507bd2d7d7478f6 --- src/static/js/contentcollector.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index b33b1e6e..e90783ae 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -518,7 +518,11 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class } if (tname == "ul" || tname == "ol") { - var type = node.attribs.class; + if(node.attribs){ + var type = node.attribs.class; + }else{ + var type = null; + } var rr = cls && /(?:^| )list-([a-z]+[12345678])\b/.exec(cls); // lists do not need to have a type, so before we make a wrong guess, check if we find a better hint within the node's children if(!rr && !type){ From 45e90e138c1abda729855f4ae301ba90783011a6 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 1 Jan 2015 16:57:31 +0000 Subject: [PATCH 262/263] v number bump --- src/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/package.json b/src/package.json index 03e892d5..b92477d6 100644 --- a/src/package.json +++ b/src/package.json @@ -54,5 +54,5 @@ "repository" : { "type" : "git", "url" : "http://github.com/ether/etherpad-lite.git" }, - "version" : "1.4.1" + "version" : "1.5.0" } From 95af55992a1fc38f473349e484df3630b6eeba79 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 1 Jan 2015 17:13:50 +0000 Subject: [PATCH 263/263] changelog --- CHANGELOG.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 052a819c..762ef39e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,34 @@ +# 1.5.0 + * NEW: Lots of performance improvements for page load times + * NEW: Hook for adding CSS to Exports + * NEW: Allow shardable socket io + * NEW: Allow UI to show when attr/prop is applied (CSS) + * NEW: Various scripts + * NEW: Export full fidelity pads (including authors etc.) + * NEW: Various front end tests + * NEW: Backend tests + * NEW: switchPad hook to instantly switch between pads + * NEW: Various translations + * NEW: Icon sets instead of images to provide quality high DPI experience + * Fix: HTML Import blocking / hanging server + * Fix: Export Bullet / Numbered lists HTML + * Fix: Swagger deprecated warning + * Fix: Bad session from crashing server + * Fix: Allow relative settings path + * Fix: Stop attributes being improperly assigned between 2 lines + * Fix: Copy / Move Pad API race condition + * Fix: Save all user preferences + * Fix: Upgrade majority of dependency inc upgrade to SocketIO1+ + * Fix: Provide UI button to restore maximized chat window + * Fix: Timeslider UI Fix + * Fix: Remove Dokuwiki + * Fix: Remove long paths from windows build (stops error during extract) + * Fix: Various globals remvoed + * Fix: Move all scripts into bin/ + * Fix: Various CSS bugfixes for Mobile devices + * Fix: Overflow Toolbar + * Fix: Line Attribute management + # 1.4.1 * NEW: Translations * NEW: userLeave Hook