From 6af160ee560d43f574ab017df7df4baca3fd8ded Mon Sep 17 00:00:00 2001 From: mluto Date: Fri, 29 Mar 2013 10:09:39 +0100 Subject: [PATCH 001/455] 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 60e0a7ac9..571ea07bf 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/455] 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 f53e9de8e..23c95c7d5 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/455] 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 ed9b258f0..2ae72fe37 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 f6adfe9ce..eaa949dec 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/455] 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 2ae72fe37..ed01600fb 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/455] 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 ed01600fb..74df5fc27 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 eaa949dec..f6adfe9ce 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/455] 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 774ebf081..05705e010 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 4ccd7131d31d931007f0606173bf212fefa30d79 Mon Sep 17 00:00:00 2001 From: Mike DeRosa Date: Sat, 14 Jun 2014 14:24:54 -0400 Subject: [PATCH 007/455] 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 26eb17a6d..fbf43599d 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 65fc8dd94..764446908 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 146ec51b5..420a6d4cf 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 73fcd3d6b..994d78450 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 008/455] 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 994d78450..7d487b1cb 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 009/455] 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 fbf43599d..d0184bc2c 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 764446908..65fc8dd94 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 420a6d4cf..146ec51b5 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 7d487b1cb..5d1f10670 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 010/455] 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 d0184bc2c..7a82e4b10 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 011/455] 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 5868af6a3..359d1f8bd 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 60fa5ffb1..8e724671f 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 8b17a6d34..bbebf0bd8 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 012/455] 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 359d1f8bd..b55ca7e07 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 013/455] 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 b55ca7e07..8b2a9c2ab 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 014/455] 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 8b2a9c2ab..5b4155bf9 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 015/455] 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 5b4155bf9..3f84af9b8 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 016/455] 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 f97d4f2b1..9f5b570d3 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 0d95c8d1ec51b5bbb3e1f6c3ae8f36e087298f18 Mon Sep 17 00:00:00 2001 From: Stefan Date: Sun, 14 Sep 2014 20:26:35 +0200 Subject: [PATCH 017/455] 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 9f5b570d3..87944a871 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 09acdda4f7e785a92eff0a6b6ef1f6b49397045b Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 14 Oct 2014 14:37:57 +0100 Subject: [PATCH 018/455] bump semver --- src/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/package.json b/src/package.json index 18556aad4..6e78884f5 100644 --- a/src/package.json +++ b/src/package.json @@ -31,7 +31,7 @@ "ejs" : "0.6.1", "graceful-fs" : "1.1.5", "slide" : "1.1.3", - "semver" : "1.0.13", + "semver" : ">2.3.0", "security" : "1.0.0", "tinycon" : "0.0.1", "underscore" : "1.3.1", From 9c2f8808bee84b818e5ba636b20bf9d605e87495 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 14 Oct 2014 14:38:47 +0100 Subject: [PATCH 019/455] bump swagger --- src/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/package.json b/src/package.json index 6e78884f5..edc44be99 100644 --- a/src/package.json +++ b/src/package.json @@ -37,7 +37,7 @@ "underscore" : "1.3.1", "unorm" : "1.0.0", "languages4translatewiki" : "0.1.3", - "swagger-node-express" : "1.2.3", + "swagger-node-express" : ">=2.1.0", "channels" : "0.0.x", "jsonminify" : "0.2.2", "measured" : "0.1.3" From 226e0bf0e39fd91e114f7020ba1dfaf1dae351c5 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 14 Oct 2014 14:41:18 +0100 Subject: [PATCH 020/455] bump resolve --- src/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/package.json b/src/package.json index edc44be99..24e27332e 100644 --- a/src/package.json +++ b/src/package.json @@ -14,7 +14,7 @@ "yajsml" : "1.1.6", "request" : "2.9.100", "require-kernel" : "1.0.5", - "resolve" : "0.2.x", + "resolve" : ">=1.0.0", "socket.io" : "0.9.x", "ueberDB" : ">=0.2.2", "express" : "3.1.0", From ac2e949c3ef1e73b19f3a8bdcd1575ed563311f0 Mon Sep 17 00:00:00 2001 From: John McLear Date: Fri, 24 Oct 2014 00:13:34 +0100 Subject: [PATCH 021/455] chrome browser change wrapping type --- src/static/js/ace2_inner.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index fb6dd0804..6a37ac7ab 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -152,7 +152,6 @@ function Ace2Inner(){ var dmesg = noop; window.dmesg = noop; - var scheduler = parent; // hack for opera required var textFace = 'monospace'; @@ -597,6 +596,13 @@ function Ace2Inner(){ fixView(); }); }, 0); + + // Chrome can't handle the truth.. If CSS rule white-space:pre-wrap + // is true then any paste event will insert two lines.. + if(browser.chrome){ + $("#innerdocbody").css({"white-space":"normal"}); + } + } function setStyled(newVal) From 9a01f905ebe7814db3d49e14b4703bcb47d91c17 Mon Sep 17 00:00:00 2001 From: Trevor Alexander Date: Tue, 28 Oct 2014 13:07:02 -1000 Subject: [PATCH 022/455] Added explicit reference to plugins and themes in readme --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 71c0b3e1e..4b739729b 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,10 @@ You can initially modify the settings in `settings.json`. (If you need to handle You should use a dedicated database such as "mysql", if you are planning on using etherpad-in a production environment, since the "dirtyDB" database driver is only for testing and/or development purposes. +## Plugins and themes + +Etherpad is very customizable through plugins. Instructions for installing themes and plugins can be found [here](https://github.com/ether/etherpad-lite/wiki/Available-Plugins). + ## Helpful resources The [wiki](https://github.com/ether/etherpad-lite/wiki) is your one-stop resource for Tutorials and How-to's, really check it out! Also, feel free to improve these wiki pages. From 69b17f508876731c415a40690650645c69ae64e4 Mon Sep 17 00:00:00 2001 From: talexand Date: Tue, 28 Oct 2014 15:13:05 -1000 Subject: [PATCH 023/455] changed link wording for readme plugin reference --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4b739729b..3ed1077f5 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ You should use a dedicated database such as "mysql", if you are planning on usin ## Plugins and themes -Etherpad is very customizable through plugins. Instructions for installing themes and plugins can be found [here](https://github.com/ether/etherpad-lite/wiki/Available-Plugins). +Etherpad is very customizable through plugins. Instructions for installing themes and plugins can be found in [the plugin wiki article](https://github.com/ether/etherpad-lite/wiki/Available-Plugins). ## Helpful resources The [wiki](https://github.com/ether/etherpad-lite/wiki) is your one-stop resource for Tutorials and How-to's, really check it out! Also, feel free to improve these wiki pages. From 3ebcaad31fc217e8ef77c940d414cb55ea99fa6d Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 1 Nov 2014 12:36:57 +0000 Subject: [PATCH 024/455] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 34a824ff2..c5186decc 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ You'll need gzip, git, curl, libssl develop libraries, python and gcc. *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. +Additionally, you'll need [node.js](http://nodejs.org) installed, Ideally the latest stable version, we recommend installing/compiling nodejs from source (avoiding apt). **As any user (we recommend creating a separate user called etherpad):** From a22b558a2c3659b7b374e262e9a6bf01c0821c36 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 1 Nov 2014 20:21:48 +0000 Subject: [PATCH 025/455] change to proper IE check --- src/static/js/pad.js | 2 +- src/static/js/pad_utils.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/static/js/pad.js b/src/static/js/pad.js index 73fcd3d6b..0f5fbe72b 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.js @@ -67,7 +67,7 @@ function createCookie(name, value, days, path){ /* Warning Internet Explorer doe } //Check if the browser is IE and if so make sure the full path is set in the cookie - if(navigator.appName=='Microsoft Internet Explorer'){ + if((navigator.appName == 'Microsoft Internet Explorer') || ((navigator.appName == 'Netscape') && (new RegExp("Trident/.*rv:([0-9]{1,}[\.0-9]{0,})").exec(navigator.userAgent) != null))){ document.cookie = name + "=" + value + expires + "; path="+document.location; } else{ diff --git a/src/static/js/pad_utils.js b/src/static/js/pad_utils.js index 343e5fce6..ff60ca7c6 100644 --- a/src/static/js/pad_utils.js +++ b/src/static/js/pad_utils.js @@ -55,7 +55,7 @@ function createCookie(name, value, days, path){ /* Used by IE */ } //Check if the browser is IE and if so make sure the full path is set in the cookie - if(navigator.appName=='Microsoft Internet Explorer'){ + if((navigator.appName == 'Microsoft Internet Explorer') || ((navigator.appName == 'Netscape') && (new RegExp("Trident/.*rv:([0-9]{1,}[\.0-9]{0,})").exec(navigator.userAgent) != null))){ document.cookie = name + "=" + value + expires + "; path=/"; /* Note this bodge fix for IE is temporary until auth is rewritten */ } else{ From a67e805da0f356cb584aa338c7c448d1969eb577 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 1 Nov 2014 21:25:49 +0000 Subject: [PATCH 026/455] basics, still not working --- src/node/hooks/express/socketio.js | 2 ++ src/package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/node/hooks/express/socketio.js b/src/node/hooks/express/socketio.js index 524bab3d9..39b325808 100644 --- a/src/node/hooks/express/socketio.js +++ b/src/node/hooks/express/socketio.js @@ -42,6 +42,7 @@ exports.expressCreateServer = function (hook_name, args, cb) { io.set('transports', settings.socketTransportProtocols ); var socketIOLogger = log4js.getLogger("socket.io"); + /* io.set('logger', { debug: function (str) { @@ -60,6 +61,7 @@ exports.expressCreateServer = function (hook_name, args, cb) { socketIOLogger.error.apply(socketIOLogger, arguments); }, }); + */ //minify socket.io javascript if(settings.minify) diff --git a/src/package.json b/src/package.json index 652aebaf8..1adbe8917 100644 --- a/src/package.json +++ b/src/package.json @@ -15,7 +15,7 @@ "request" : "2.9.100", "require-kernel" : "1.0.5", "resolve" : ">=1.0.0", - "socket.io" : "0.9.x", + "socket.io" : ">=1.2.0", "ueberDB" : ">=0.2.6", "express" : "3.1.0", "async" : "0.1.x", From 2c801cc558bd358d1de6a0cdfcd3de8201739f7b Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 1 Nov 2014 22:36:19 +0000 Subject: [PATCH 027/455] no errors but no connections --- src/node/hooks/express/socketio.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/node/hooks/express/socketio.js b/src/node/hooks/express/socketio.js index 39b325808..17ce3864a 100644 --- a/src/node/hooks/express/socketio.js +++ b/src/node/hooks/express/socketio.js @@ -16,6 +16,7 @@ exports.expressCreateServer = function (hook_name, args, cb) { /* Require an express session cookie to be present, and load the * session. See http://www.danielbaulig.de/socket-ioexpress for more * info */ + /* io.set('authorization', function (data, accept) { if (!data.headers.cookie) return accept('No session cookie transmitted.', false); @@ -35,11 +36,12 @@ exports.expressCreateServer = function (hook_name, args, cb) { }); }); }); + */ // 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 - io.set('transports', settings.socketTransportProtocols ); + // io.set('transports', settings.socketTransportProtocols ); var socketIOLogger = log4js.getLogger("socket.io"); /* @@ -65,7 +67,7 @@ exports.expressCreateServer = function (hook_name, args, cb) { //minify socket.io javascript if(settings.minify) - io.enable('browser client minification'); + // io.enable('browser client minification'); //Initalize the Socket.IO Router socketIORouter.setSocketIO(io); From c7061e47db8aa7828461fc3344e69a6b4dc6d319 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 4 Nov 2014 17:06:47 +0000 Subject: [PATCH 028/455] use request and a remote ep plugin only endpoint --- src/static/js/pluginfw/installer.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/static/js/pluginfw/installer.js b/src/static/js/pluginfw/installer.js index 9f7ac939f..7480e152d 100644 --- a/src/static/js/pluginfw/installer.js +++ b/src/static/js/pluginfw/installer.js @@ -1,6 +1,7 @@ var plugins = require("ep_etherpad-lite/static/js/pluginfw/plugins"); var hooks = require("ep_etherpad-lite/static/js/pluginfw/hooks"); var npm = require("npm"); +var request = require("request"); var npmIsLoaded = false; var withNpm = function (npmfn) { @@ -60,17 +61,16 @@ exports.availablePlugins = null; var cacheTimestamp = 0; exports.getAvailablePlugins = function(maxCacheAge, cb) { - withNpm(function (er) { + request("http://etherpad.org/plugins.json", function(er, response, plugins){ if (er) return cb && cb(er); if(exports.availablePlugins && maxCacheAge && Math.round(+new Date/1000)-cacheTimestamp <= maxCacheAge) { return cb && cb(null, exports.availablePlugins) } - npm.commands.search(['ep_'], /*silent?*/true, function(er, results) { - if(er) return cb && cb(er); - exports.availablePlugins = results; + console.log("plugins", plugins); + plugins = JSON.parse(plugins); + exports.availablePlugins = plugins; cacheTimestamp = Math.round(+new Date/1000); - cb && cb(null, results) - }) + cb && cb(null, plugins) }); }; From 1e53c4f5f0440b00a30037793783f862f3cced7d Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 4 Nov 2014 17:57:18 +0000 Subject: [PATCH 029/455] identify which parts are causing issues and comment them out, obviously this needs fixing --- src/node/handler/PadMessageHandler.js | 8 +++++++- src/node/handler/SocketIORouter.js | 6 ++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index e1ac994e8..c81c9d17b 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -1015,6 +1015,7 @@ function handleClientReady(client, message) return callback(); //Check if this author is already on the pad, if yes, kick the other sessions! +/* var roomClients = socketio.sockets.clients(padIds.padId); for(var i = 0; i < roomClients.length; i++) { var sinfo = sessioninfos[roomClients[i].id]; @@ -1025,6 +1026,7 @@ function handleClientReady(client, message) roomClients[i].json.send({disconnect:"userdup"}); } } +*/ //Save in sessioninfos that this session belonges to this pad sessioninfos[client.id].padId = padIds.padId; @@ -1032,6 +1034,7 @@ function handleClientReady(client, message) sessioninfos[client.id].readonly = padIds.readonly; //Log creation/(re-)entering of a pad +/* client.get('remoteAddress', function(er, ip) { //Anonymize the IP address if IP logging is disabled if(settings.disableIPlogging) { @@ -1045,6 +1048,7 @@ function handleClientReady(client, message) accessLogger.info('[CREATE] Pad "'+padIds.padId+'": Client '+client.id+' with IP "'+ip+'" created the pad'); } }) +*/ //If this is a reconnect, we don't have to send the client the ClientVars again if(message.reconnect == true) @@ -1100,7 +1104,7 @@ function handleClientReady(client, message) // tell the client the number of the latest chat-message, which will be // used to request the latest 100 chat-messages later (GET_CHAT_MESSAGES) "chatHead": pad.chatHead, - "numConnectedUsers": roomClients.length, + "numConnectedUsers": 0, "readOnlyId": padIds.readOnlyPadId, "readonly": padIds.readonly, "serverTimestamp": new Date().getTime(), @@ -1165,6 +1169,7 @@ function handleClientReady(client, message) client.broadcast.to(padIds.padId).json.send(messageToTheOtherUsers); //Run trough all sessions of this pad +/* async.forEach(socketio.sockets.clients(padIds.padId), function(roomClient, callback) { var author; @@ -1211,6 +1216,7 @@ function handleClientReady(client, message) } ], callback); }, callback); +*/ } ],function(err) { diff --git a/src/node/handler/SocketIORouter.js b/src/node/handler/SocketIORouter.js index b3e046d2c..96260254d 100644 --- a/src/node/handler/SocketIORouter.js +++ b/src/node/handler/SocketIORouter.js @@ -56,11 +56,13 @@ exports.setSocketIO = function(_socket) { socket.sockets.on('connection', function(client) { + +// Broken: See http://stackoverflow.com/questions/4647348/send-message-to-specific-client-with-socket-io-and-node-js if(settings.trustProxy && client.handshake.headers['x-forwarded-for'] !== undefined){ - client.set('remoteAddress', client.handshake.headers['x-forwarded-for']); +// client.set('remoteAddress', client.handshake.headers['x-forwarded-for']); } else{ - client.set('remoteAddress', client.handshake.address.address); +// client.set('remoteAddress', client.handshake.address); } var clientAuthorized = false; From adef0af227a765b8d2ddfece29449bf6b4053313 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 4 Nov 2014 18:01:41 +0000 Subject: [PATCH 030/455] more stuff required to stop it crashing, now we have identified what needs fixing we can fix it --- src/node/handler/PadMessageHandler.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index c81c9d17b..da2a7a144 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -115,6 +115,7 @@ exports.handleDisconnect = function(client) //if this connection was already etablished with a handshake, send a disconnect message to the others if(session && session.author) { + /* client.get('remoteAddress', function(er, ip) { //Anonymize the IP address if IP logging is disabled if(settings.disableIPlogging) { @@ -123,6 +124,7 @@ exports.handleDisconnect = function(client) accessLogger.info('[LEAVE] Pad "'+session.padId+'": Author "'+session.author+'" on client '+client.id+' with IP "'+ip+'" left the pad') }) + */ //get the author color out of the db authorManager.getAuthorColorId(session.author, function(err, color) @@ -752,6 +754,7 @@ function handleUserChanges(data, cb) exports.updatePadClients = function(pad, callback) { + /* //skip this step if noone is on this pad var roomClients = socketio.sockets.clients(pad.id); if(roomClients.length==0) @@ -827,6 +830,7 @@ exports.updatePadClients = function(pad, callback) callback ); },callback); + */ } /** From 5d0ccb5f8f7b029ed71b0cd5bc668a3fe088a96f Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 4 Nov 2014 18:17:39 +0000 Subject: [PATCH 031/455] auth fix --- src/node/hooks/express/socketio.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/node/hooks/express/socketio.js b/src/node/hooks/express/socketio.js index 17ce3864a..32c5fd889 100644 --- a/src/node/hooks/express/socketio.js +++ b/src/node/hooks/express/socketio.js @@ -16,8 +16,9 @@ exports.expressCreateServer = function (hook_name, args, cb) { /* Require an express session cookie to be present, and load the * session. See http://www.danielbaulig.de/socket-ioexpress for more * info */ - /* - io.set('authorization', function (data, accept) { + + io.use(function(socket, accept) { + var data = socket.request; if (!data.headers.cookie) return accept('No session cookie transmitted.', false); // Use connect's cookie parser, because it knows how to parse signed cookies @@ -36,7 +37,6 @@ exports.expressCreateServer = function (hook_name, args, cb) { }); }); }); - */ // there shouldn't be a browser that isn't compatible to all // transports in this list at once From cb28d109631e7bd5357fcd754b60fd89e820e7e6 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 4 Nov 2014 18:32:26 +0000 Subject: [PATCH 032/455] move logging into debug environment variable .. sighs --- src/node/utils/Settings.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/node/utils/Settings.js b/src/node/utils/Settings.js index c455617b0..1f22ccfd7 100644 --- a/src/node/utils/Settings.js +++ b/src/node/utils/Settings.js @@ -228,6 +228,7 @@ exports.reloadSettings = function reloadSettings() { log4js.configure(exports.logconfig);//Configure the logging appenders log4js.setGlobalLogLevel(exports.loglevel);//set loglevel + process.env['DEBUG'] = 'socket.io:' + exports.loglevel; // Used by SocketIO for Debug log4js.replaceConsole(); if(!exports.sessionKey){ // If the secretKey isn't set we also create yet another unique value here From 95e7b0f15609fc850b71717a672abd02237a0f33 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 4 Nov 2014 19:11:06 +0000 Subject: [PATCH 033/455] transports --- src/node/hooks/express/socketio.js | 39 +++++++++--------------------- 1 file changed, 12 insertions(+), 27 deletions(-) diff --git a/src/node/hooks/express/socketio.js b/src/node/hooks/express/socketio.js index 32c5fd889..98146bbbb 100644 --- a/src/node/hooks/express/socketio.js +++ b/src/node/hooks/express/socketio.js @@ -1,10 +1,17 @@ var log4js = require('log4js'); -var socketio = require('socket.io'); var settings = require('../../utils/Settings'); 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'); @@ -38,32 +45,10 @@ exports.expressCreateServer = function (hook_name, args, cb) { }); }); - // 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 - // io.set('transports', settings.socketTransportProtocols ); - - var socketIOLogger = log4js.getLogger("socket.io"); - /* - io.set('logger', { - debug: function (str) - { - socketIOLogger.debug.apply(socketIOLogger, arguments); - }, - info: function (str) - { - socketIOLogger.info.apply(socketIOLogger, arguments); - }, - warn: function (str) - { - socketIOLogger.warn.apply(socketIOLogger, arguments); - }, - error: function (str) - { - socketIOLogger.error.apply(socketIOLogger, arguments); - }, - }); - */ + // var socketIOLogger = log4js.getLogger("socket.io"); + // Debug logging now has to be set at an environment level, this is stupid. + // https://github.com/Automattic/socket.io/wiki/Migrating-to-1.0 + // This debug logging environment is set in Settings.js //minify socket.io javascript if(settings.minify) From 68eaa09708241b0551f5d345f45d8f7ba00913d2 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 4 Nov 2014 19:55:05 +0000 Subject: [PATCH 034/455] Begin to repair damage done to rooms logic --- src/node/handler/PadMessageHandler.js | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index da2a7a144..2072a784b 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -754,9 +754,14 @@ function handleUserChanges(data, cb) exports.updatePadClients = function(pad, callback) { - /* //skip this step if noone is on this pad - var roomClients = socketio.sockets.clients(pad.id); + var roomClients = [], room = socketio.sockets.adapter.rooms[pad.id]; + if (room) { + for (var id in room) { + roomClients.push(socketio.sockets.adapter.nsp.connected[id]); + } + } + if(roomClients.length==0) return callback(); @@ -769,10 +774,8 @@ exports.updatePadClients = function(pad, callback) var revCache = {}; //go trough all sessions on this pad - async.forEach(roomClients, function(client, callback) - { + async.forEach(roomClients, function(client, callback){ var sid = client.id; - //https://github.com/caolan/async#whilst //send them all new changesets async.whilst( @@ -802,7 +805,8 @@ exports.updatePadClients = function(pad, callback) if(author == sessioninfos[sid].author) { - client.json.send({"type":"COLLABROOM","data":{type:"ACCEPT_COMMIT", newRev:r}}); + // client.json.send({"type":"COLLABROOM","data":{type:"ACCEPT_COMMIT", newRev:r}}); + socketio.in(pad).send({"type":"COLLABROOM","data":{type:"ACCEPT_COMMIT", newRev:r}}); } else { @@ -817,7 +821,9 @@ exports.updatePadClients = function(pad, callback) timeDelta: currentTime - sessioninfos[sid].time }}; - client.json.send(wireMsg); + // client.json.send(wireMsg); + socketio.in(pad).send({"type":"COLLABROOM","data":{type:"ACCEPT_COMMIT", newRev:r}}); + } sessioninfos[sid].time = currentTime; @@ -830,7 +836,6 @@ exports.updatePadClients = function(pad, callback) callback ); },callback); - */ } /** From a6cbb4af409b5448e2b27d4f28234efd8c0e3c17 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 4 Nov 2014 19:58:38 +0000 Subject: [PATCH 035/455] working messages --- src/node/handler/PadMessageHandler.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index 2072a784b..0dc18d7e0 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -805,8 +805,7 @@ exports.updatePadClients = function(pad, callback) if(author == sessioninfos[sid].author) { - // client.json.send({"type":"COLLABROOM","data":{type:"ACCEPT_COMMIT", newRev:r}}); - socketio.in(pad).send({"type":"COLLABROOM","data":{type:"ACCEPT_COMMIT", newRev:r}}); + client.json.send({"type":"COLLABROOM","data":{type:"ACCEPT_COMMIT", newRev:r}}); } else { @@ -821,9 +820,7 @@ exports.updatePadClients = function(pad, callback) timeDelta: currentTime - sessioninfos[sid].time }}; - // client.json.send(wireMsg); - socketio.in(pad).send({"type":"COLLABROOM","data":{type:"ACCEPT_COMMIT", newRev:r}}); - + client.json.send(wireMsg); } sessioninfos[sid].time = currentTime; From 8391f902fbda5f687bb9efd3152352ddcdc11a81 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 4 Nov 2014 22:31:09 +0000 Subject: [PATCH 036/455] re-apply roomsize --- 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 0dc18d7e0..af83dfac4 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -1110,7 +1110,7 @@ function handleClientReady(client, message) // tell the client the number of the latest chat-message, which will be // used to request the latest 100 chat-messages later (GET_CHAT_MESSAGES) "chatHead": pad.chatHead, - "numConnectedUsers": 0, + "numConnectedUsers": roomClients.length, "readOnlyId": padIds.readOnlyPadId, "readonly": padIds.readonly, "serverTimestamp": new Date().getTime(), From af3e8e5e15b66ccae2306ba6b956d8acd8143eed Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 4 Nov 2014 22:34:43 +0000 Subject: [PATCH 037/455] fix room client leave --- src/node/handler/PadMessageHandler.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index af83dfac4..8657460c5 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -1021,8 +1021,13 @@ function handleClientReady(client, message) return callback(); //Check if this author is already on the pad, if yes, kick the other sessions! -/* - var roomClients = socketio.sockets.clients(padIds.padId); + var roomClients = [], room = socketio.sockets.adapter.rooms[pad.id]; + 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 == author) { @@ -1032,7 +1037,6 @@ function handleClientReady(client, message) roomClients[i].json.send({disconnect:"userdup"}); } } -*/ //Save in sessioninfos that this session belonges to this pad sessioninfos[client.id].padId = padIds.padId; From f418dfa2053c4b40bd46d586b960a2de8167388c Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 4 Nov 2014 22:37:10 +0000 Subject: [PATCH 038/455] fix author notification handler --- src/node/handler/PadMessageHandler.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index 8657460c5..3c39c4d86 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -1179,8 +1179,14 @@ function handleClientReady(client, message) client.broadcast.to(padIds.padId).json.send(messageToTheOtherUsers); //Run trough all sessions of this pad -/* - async.forEach(socketio.sockets.clients(padIds.padId), function(roomClient, callback) + var roomClients = [], room = socketio.sockets.adapter.rooms[pad.id]; + if (room) { + for (var id in room) { + roomClients.push(socketio.sockets.adapter.nsp.connected[id]); + } + } + + async.forEach(roomClients, function(roomClient, callback) { var author; @@ -1226,7 +1232,6 @@ function handleClientReady(client, message) } ], callback); }, callback); -*/ } ],function(err) { From 9fa77cdea251b5e32a351298bbe97a88abec3e56 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 4 Nov 2014 23:25:18 +0000 Subject: [PATCH 039/455] working handling of setting client ip and anonymizing etc --- src/node/handler/PadMessageHandler.js | 21 ++++++++++++--------- src/node/handler/SocketIORouter.js | 9 ++++++--- src/node/hooks/express/socketio.js | 8 +++++--- src/node/utils/RemoteAddress.js | 1 + 4 files changed, 24 insertions(+), 15 deletions(-) create mode 100644 src/node/utils/RemoteAddress.js diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index 3c39c4d86..b4e6243f0 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -37,6 +37,7 @@ var _ = require('underscore'); var hooks = require("ep_etherpad-lite/static/js/pluginfw/hooks.js"); var channels = require("channels"); var stats = require('../stats'); +var remoteAddress = require("../utils/RemoteAddress").remoteAddress; /** * A associative array that saves informations about a session @@ -115,16 +116,18 @@ exports.handleDisconnect = function(client) //if this connection was already etablished with a handshake, send a disconnect message to the others if(session && session.author) { - /* - client.get('remoteAddress', function(er, ip) { - //Anonymize the IP address if IP logging is disabled - if(settings.disableIPlogging) { - ip = 'ANONYMOUS'; - } - accessLogger.info('[LEAVE] Pad "'+session.padId+'": Author "'+session.author+'" on client '+client.id+' with IP "'+ip+'" left the pad') - }) - */ +console.log(remoteAddress); + + // Get the IP address from our persistant object + var ip = remoteAddress[client.id]; + + // Anonymize the IP address if IP logging is disabled + if(settings.disableIPlogging) { + ip = 'ANONYMOUS'; + } + + accessLogger.info('[LEAVE] Pad "'+session.padId+'": Author "'+session.author+'" on client '+client.id+' with IP "'+ip+'" left the pad') //get the author color out of the db authorManager.getAuthorColorId(session.author, function(err, color) diff --git a/src/node/handler/SocketIORouter.js b/src/node/handler/SocketIORouter.js index 96260254d..0a7361f42 100644 --- a/src/node/handler/SocketIORouter.js +++ b/src/node/handler/SocketIORouter.js @@ -24,6 +24,7 @@ var log4js = require('log4js'); var messageLogger = log4js.getLogger("message"); var securityManager = require("../db/SecurityManager"); var readOnlyManager = require("../db/ReadOnlyManager"); +var remoteAddress = require("../utils/RemoteAddress").remoteAddress; var settings = require('../utils/Settings'); /** @@ -57,12 +58,14 @@ exports.setSocketIO = function(_socket) { socket.sockets.on('connection', function(client) { -// Broken: See http://stackoverflow.com/questions/4647348/send-message-to-specific-client-with-socket-io-and-node-js + // Broken: See http://stackoverflow.com/questions/4647348/send-message-to-specific-client-with-socket-io-and-node-js + // Fixed by having a persistant object, ideally this would actually be in the database layer + // TODO move to database layer if(settings.trustProxy && client.handshake.headers['x-forwarded-for'] !== undefined){ -// client.set('remoteAddress', client.handshake.headers['x-forwarded-for']); + remoteAddress[client.id] = client.handshake.headers['x-forwarded-for']; } else{ -// client.set('remoteAddress', client.handshake.address); + remoteAddress[client.id] = client.handshake.address; } var clientAuthorized = false; diff --git a/src/node/hooks/express/socketio.js b/src/node/hooks/express/socketio.js index 98146bbbb..e88a3f4c0 100644 --- a/src/node/hooks/express/socketio.js +++ b/src/node/hooks/express/socketio.js @@ -51,9 +51,11 @@ exports.expressCreateServer = function (hook_name, args, cb) { // This debug logging environment is set in Settings.js //minify socket.io javascript - if(settings.minify) - // io.enable('browser client minification'); - + // Due to a shitty decision by the SocketIO team minification is + // no longer available, details available at: + // http://stackoverflow.com/questions/23981741/minify-socket-io-socket-io-js-with-1-0 + // if(settings.minify) io.enable('browser client minification'); + //Initalize the Socket.IO Router socketIORouter.setSocketIO(io); socketIORouter.addComponent("pad", padMessageHandler); diff --git a/src/node/utils/RemoteAddress.js b/src/node/utils/RemoteAddress.js new file mode 100644 index 000000000..86a4a5b26 --- /dev/null +++ b/src/node/utils/RemoteAddress.js @@ -0,0 +1 @@ +exports.remoteAddress = {}; From 053ff3097d097ad89337740a0209c50f4726399d Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 4 Nov 2014 23:29:45 +0000 Subject: [PATCH 040/455] whoops I missed this one --- src/node/handler/PadMessageHandler.js | 28 ++++++++++++--------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index b4e6243f0..36da4842b 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -117,8 +117,6 @@ exports.handleDisconnect = function(client) if(session && session.author) { -console.log(remoteAddress); - // Get the IP address from our persistant object var ip = remoteAddress[client.id]; @@ -1047,21 +1045,19 @@ function handleClientReady(client, message) sessioninfos[client.id].readonly = padIds.readonly; //Log creation/(re-)entering of a pad -/* - client.get('remoteAddress', function(er, ip) { - //Anonymize the IP address if IP logging is disabled - if(settings.disableIPlogging) { - ip = 'ANONYMOUS'; - } + var ip = remoteAddress[client.id]; - if(pad.head > 0) { - accessLogger.info('[ENTER] Pad "'+padIds.padId+'": Client '+client.id+' with IP "'+ip+'" entered the pad'); - } - else if(pad.head == 0) { - accessLogger.info('[CREATE] Pad "'+padIds.padId+'": Client '+client.id+' with IP "'+ip+'" created the pad'); - } - }) -*/ + //Anonymize the IP address if IP logging is disabled + if(settings.disableIPlogging) { + ip = 'ANONYMOUS'; + } + + if(pad.head > 0) { + accessLogger.info('[ENTER] Pad "'+padIds.padId+'": Client '+client.id+' with IP "'+ip+'" entered the pad'); + } + else if(pad.head == 0) { + accessLogger.info('[CREATE] Pad "'+padIds.padId+'": Client '+client.id+' with IP "'+ip+'" created the pad'); + } //If this is a reconnect, we don't have to send the client the ClientVars again if(message.reconnect == true) From 57f56d8fe11ee28b9f3f2a1e969f86038ca27d34 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 4 Nov 2014 23:42:40 +0000 Subject: [PATCH 041/455] bump express to a working version --- src/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/package.json b/src/package.json index 1adbe8917..4639abcfe 100644 --- a/src/package.json +++ b/src/package.json @@ -17,7 +17,7 @@ "resolve" : ">=1.0.0", "socket.io" : ">=1.2.0", "ueberDB" : ">=0.2.6", - "express" : "3.1.0", + "express" : ">3.1.0 <3.9.0", "async" : "0.1.x", "connect" : "2.7.x", "clean-css" : "0.3.2", From 7d04d7309f872dca337c8a648aff9f1aee5cd370 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 6 Nov 2014 14:32:20 +0000 Subject: [PATCH 042/455] reflow buttons based on browser size, much better --- src/static/css/pad.css | 43 +++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/src/static/css/pad.css b/src/static/css/pad.css index 599b9fd41..228c035fd 100644 --- a/src/static/css/pad.css +++ b/src/static/css/pad.css @@ -59,7 +59,7 @@ a img { height: 32px; } .toolbar ul { - position: relative; + position: absolute; list-style: none; padding-right: 3px; padding-left: 1px; @@ -67,9 +67,6 @@ a img { overflow: hidden; float: left } -.toolbar ul.menu_right { - float: right -} .toolbar ul li { float: left; margin-left: 2px; @@ -84,6 +81,7 @@ a img { visibility: hidden; width: 0px; padding: 5px; + height:20px; } .toolbar ul li a:hover { text-decoration: none; @@ -164,6 +162,15 @@ a img { border: 1px solid #ccc; outline: none; } +.toolbar ul.menu_left { + left:0px; + right:250px; +} + +.toolbar ul.menu_right { + right:0px; +} + li[data-key=showusers] > a { min-width: 30px; text-align: left; @@ -843,12 +850,16 @@ input[type=checkbox] { width: 185px !important; } @media screen and (max-width: 600px) { - .toolbar ul li.separator { - display: none; - } - .toolbar ul li a { - padding: 4px 1px - } + .toolbar ul li.separator { + display: none; + } + .toolbar ul li a { + padding: 4px 1px + } + .toolbar ul.menu_left { + left:0px; + right:150px; + } } @media all and (max-width: 400px){ #gritter-notice-wrapper{ @@ -894,9 +905,13 @@ input[type=checkbox] { #editbar { height: 62px; } + .toolbar ul.menu_left { + left:0px; + right:100px; + } + .toolbar ul.menu_right { - float: left; - margin-top:2px; + right:0px; } .popup { width:100%; @@ -917,6 +932,8 @@ input[type=checkbox] { #editorcontainer { margin-bottom: 33px } + .toolbar ul.menu_left { + } .toolbar ul.menu_right { background: #f7f7f7; background: -webkit-linear-gradient(#f7f7f7, #f1f1f1 80%); @@ -1076,4 +1093,4 @@ input[type=checkbox] { text-shadow: none; } -/* End of gritter stuff */ \ No newline at end of file +/* End of gritter stuff */ From 9170effb27dee076ac7401a34f0e857be50330eb Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 6 Nov 2014 15:09:44 +0000 Subject: [PATCH 043/455] working redraw and draw on startup --- src/static/js/pad.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/static/js/pad.js b/src/static/js/pad.js index 73fcd3d6b..5292e75d7 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.js @@ -370,6 +370,18 @@ function handshake() $('#readonlyinput').on('click',function(){ padeditbar.setEmbedLinks(); }); + // Listen for resize events (sucks but needed as iFrame ace_inner has to be position absolute + // A CSS fix for this would be nice but I'm not sure how we'd do it. + $(window).resize(function(){ + redrawEditbar(); + }); + redrawEditbar(); +} + +var redrawEditbar = function(){ + var height = $('.menu_left').height() + 4 + "px"; + $('#editbar').css("height", height); + $('#editorcontainer').css("top", height); } $.extend($.gritter.options, { @@ -937,4 +949,3 @@ exports.handshake = handshake; exports.pad = pad; exports.init = init; exports.alertBar = alertBar; - From 96d6e7c1b7df59e96c394608a343cb698c848576 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 6 Nov 2014 15:12:40 +0000 Subject: [PATCH 044/455] cleaner reflow --- src/static/js/pad.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/static/js/pad.js b/src/static/js/pad.js index 5292e75d7..6a3a2e0ec 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.js @@ -379,9 +379,10 @@ function handshake() } var redrawEditbar = function(){ - var height = $('.menu_left').height() + 4 + "px"; - $('#editbar').css("height", height); - $('#editorcontainer').css("top", height); + var editbarHeight = $('.menu_left').height() + 2 + "px"; + var containerTop = $('.menu_left').height() + 5 + "px"; + $('#editbar').css("height", editbarHeight); + $('#editorcontainer').css("top", containerTop); } $.extend($.gritter.options, { From 0685e563ed947a42377fe1a5eb6fca10f3e8e833 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 6 Nov 2014 15:43:21 +0000 Subject: [PATCH 045/455] working on resize --- src/static/css/pad.css | 1 + src/static/js/pad.js | 13 ------------- src/static/js/pad_editbar.js | 15 ++++++++++++++- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/static/css/pad.css b/src/static/css/pad.css index 228c035fd..11705e06b 100644 --- a/src/static/css/pad.css +++ b/src/static/css/pad.css @@ -933,6 +933,7 @@ input[type=checkbox] { margin-bottom: 33px } .toolbar ul.menu_left { + right:0px; } .toolbar ul.menu_right { background: #f7f7f7; diff --git a/src/static/js/pad.js b/src/static/js/pad.js index 6a3a2e0ec..649d8c825 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.js @@ -370,19 +370,6 @@ function handshake() $('#readonlyinput').on('click',function(){ padeditbar.setEmbedLinks(); }); - // Listen for resize events (sucks but needed as iFrame ace_inner has to be position absolute - // A CSS fix for this would be nice but I'm not sure how we'd do it. - $(window).resize(function(){ - redrawEditbar(); - }); - redrawEditbar(); -} - -var redrawEditbar = function(){ - var editbarHeight = $('.menu_left').height() + 2 + "px"; - var containerTop = $('.menu_left').height() + 5 + "px"; - $('#editbar').css("height", editbarHeight); - $('#editorcontainer').css("top", containerTop); } $.extend($.gritter.options, { diff --git a/src/static/js/pad_editbar.js b/src/static/js/pad_editbar.js index 6352b1296..c067bbbad 100644 --- a/src/static/js/pad_editbar.js +++ b/src/static/js/pad_editbar.js @@ -140,7 +140,12 @@ var padeditbar = (function() init: function() { var self = this; self.dropdowns = []; - + // Listen for resize events (sucks but needed as iFrame ace_inner has to be position absolute + // A CSS fix for this would be nice but I'm not sure how we'd do it. + $(window).resize(function(){ + self.redrawHeight(); + }); + $("#editbar .editbarbutton").attr("unselectable", "on"); // for IE $("#editbar").removeClass("disabledtoolbar").addClass("enabledtoolbar"); $("#editbar [data-key]").each(function () { @@ -149,6 +154,8 @@ var padeditbar = (function() }); }); + this.redrawHeight(); + registerDefaultCommands(self); hooks.callAll("postToolbarInit", { @@ -170,6 +177,12 @@ var padeditbar = (function() this.commands[cmd] = callback; return this; }, + redrawHeight: function(){ + var editbarHeight = $('.menu_left').height() + 2 + "px"; + var containerTop = $('.menu_left').height() + 5 + "px"; + $('#editbar').css("height", editbarHeight); + $('#editorcontainer').css("top", containerTop); + }, registerDropdownCommand: function (cmd, dropdown) { dropdown = dropdown || cmd; self.dropdowns.push(dropdown) From d13b6162f0b7869942ec4bcb6ab7b5267b448b86 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 6 Nov 2014 15:50:55 +0000 Subject: [PATCH 046/455] this looks way better --- src/static/css/pad.css | 3 +++ src/static/js/pad_editbar.js | 2 ++ 2 files changed, 5 insertions(+) diff --git a/src/static/css/pad.css b/src/static/css/pad.css index 11705e06b..91c97c5e1 100644 --- a/src/static/css/pad.css +++ b/src/static/css/pad.css @@ -182,6 +182,9 @@ li[data-key=showusers] > a #online_count { top: 2px; padding-left: 2px; } +#editbar{ + display:none; +} #editorcontainer { position: absolute; top: 37px; /* + 1px border */ diff --git a/src/static/js/pad_editbar.js b/src/static/js/pad_editbar.js index c067bbbad..d39bfecd7 100644 --- a/src/static/js/pad_editbar.js +++ b/src/static/js/pad_editbar.js @@ -154,6 +154,8 @@ var padeditbar = (function() }); }); + $('#editbar').show(); + this.redrawHeight(); registerDefaultCommands(self); From fc5152c2f3199ec6056fc97a04eda3afd733eae0 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 6 Nov 2014 17:38:38 +0000 Subject: [PATCH 047/455] remove logging --- src/static/js/pluginfw/installer.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/static/js/pluginfw/installer.js b/src/static/js/pluginfw/installer.js index 7480e152d..c1a9adad2 100644 --- a/src/static/js/pluginfw/installer.js +++ b/src/static/js/pluginfw/installer.js @@ -66,7 +66,6 @@ exports.getAvailablePlugins = function(maxCacheAge, cb) { if(exports.availablePlugins && maxCacheAge && Math.round(+new Date/1000)-cacheTimestamp <= maxCacheAge) { return cb && cb(null, exports.availablePlugins) } - console.log("plugins", plugins); plugins = JSON.parse(plugins); exports.availablePlugins = plugins; cacheTimestamp = Math.round(+new Date/1000); From d246a191c6cc9a6e2d84451058e2252f7c552095 Mon Sep 17 00:00:00 2001 From: Cristo Date: Sat, 8 Nov 2014 01:12:40 +0100 Subject: [PATCH 048/455] Added option to restore revisions #1791 --- src/node/db/API.js | 98 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/src/node/db/API.js b/src/node/db/API.js index 4a9123682..2aadc4837 100644 --- a/src/node/db/API.js +++ b/src/node/db/API.js @@ -576,6 +576,104 @@ exports.deletePad = function(padID, callback) }); } +exports.restoreRevision = function(padID, rev, callback) +{ + var Changeset = require("ep_etherpad-lite/static/js/Changeset"); + + //check if rev is a number + if(rev !== undefined && typeof rev != "number") + { + //try to parse the number + if(!isNaN(parseInt(rev))) + { + rev = parseInt(rev); + } + else + { + callback(new customError("rev is not a number", "apierror")); + return; + } + } + + //ensure this is not a negativ number + if(rev !== undefined && rev < 0) + { + callback(new customError("rev is a negativ number","apierror")); + return; + } + + //ensure this is not a float value + if(rev !== undefined && !is_int(rev)) + { + callback(new customError("rev is a float value","apierror")); + return; + } + + //get the pad + getPadSafe(padID, true, function(err, pad) + { + if(ERR(err, callback)) return; + + + //check if this is a valid revision + if(rev > pad.getHeadRevisionNumber()) + { + callback(new customError("rev is higher than the head revision of the pad","apierror")); + return; + } + + pad.getInternalRevisionAText(rev, function(err, atext) + { + if(ERR(err, callback)) return; + + var oldText = pad.text(); + atext.text += "\n"; + function eachAttribRun(attribs, func) + { + var attribsIter = Changeset.opIterator(attribs); + var textIndex = 0; + var newTextStart = 0; + var newTextEnd = atext.text.length; + while (attribsIter.hasNext()) + { + var op = attribsIter.next(); + var nextIndex = textIndex + op.chars; + if (!(nextIndex <= newTextStart || textIndex >= newTextEnd)) + { + func(Math.max(newTextStart, textIndex), Math.min(newTextEnd, nextIndex), op.attribs); + } + textIndex = nextIndex; + } + } + + // create a new changeset with a helper builder object + var builder = Changeset.builder(oldText.length); + + // assemble each line into the builder + eachAttribRun(atext.attribs, function(start, end, attribs) + { + builder.insert(atext.text.substring(start, end), attribs); + }); + + var lastNewlinePos = oldText.lastIndexOf('\n'); + if (lastNewlinePos < 0) { + builder.remove(oldText.length-1,0); + } else { + builder.remove(lastNewlinePos, oldText.match(/\n/g).length-1); + builder.remove(oldText.length - lastNewlinePos-1,0); + } + + var changeset = builder.toString(); + + //append the changeset + pad.appendRevision(changeset); + // + callback(null, changeset); + }); + + }); +}; + /** copyPad(sourceID, destinationID[, force=false]) copies a pad. If force is true, the destination will be overwritten if it exists. From c33c6e085ec533056cb5bf75dc76350e9cdf03ba Mon Sep 17 00:00:00 2001 From: Cristo Date: Sat, 8 Nov 2014 01:39:27 +0100 Subject: [PATCH 049/455] comment addded --- src/node/db/API.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/node/db/API.js b/src/node/db/API.js index 2aadc4837..8ad6e2eb2 100644 --- a/src/node/db/API.js +++ b/src/node/db/API.js @@ -575,7 +575,14 @@ exports.deletePad = function(padID, callback) pad.remove(callback); }); } +/** + restoreRevision(padID, [rev]) Restores revision from past as new changeset + Example returns: + + {code:0, message:"ok", data:null} + {code: 1, message:"padID does not exist", data: null} + */ exports.restoreRevision = function(padID, rev, callback) { var Changeset = require("ep_etherpad-lite/static/js/Changeset"); @@ -668,7 +675,7 @@ exports.restoreRevision = function(padID, rev, callback) //append the changeset pad.appendRevision(changeset); // - callback(null, changeset); + callback(null, null); }); }); From 46bc328896877630169c4cea60431be981859352 Mon Sep 17 00:00:00 2001 From: Cristo Date: Sat, 8 Nov 2014 01:41:23 +0100 Subject: [PATCH 050/455] new api ver --- src/node/handler/APIHandler.js | 48 +++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/src/node/handler/APIHandler.js b/src/node/handler/APIHandler.js index 273a58a6a..76af4aa1e 100644 --- a/src/node/handler/APIHandler.js +++ b/src/node/handler/APIHandler.js @@ -345,10 +345,56 @@ var version = , "getChatHistory" : ["padID", "start", "end"] , "getChatHead" : ["padID"] } + , "1.2.11": + { "createGroup" : [] + , "createGroupIfNotExistsFor" : ["groupMapper"] + , "deleteGroup" : ["groupID"] + , "listPads" : ["groupID"] + , "listAllPads" : [] + , "createDiffHTML" : ["padID", "startRev", "endRev"] + , "createPad" : ["padID", "text"] + , "createGroupPad" : ["groupID", "padName", "text"] + , "createAuthor" : ["name"] + , "createAuthorIfNotExistsFor": ["authorMapper" , "name"] + , "listPadsOfAuthor" : ["authorID"] + , "createSession" : ["groupID", "authorID", "validUntil"] + , "deleteSession" : ["sessionID"] + , "getSessionInfo" : ["sessionID"] + , "listSessionsOfGroup" : ["groupID"] + , "listSessionsOfAuthor" : ["authorID"] + , "getText" : ["padID", "rev"] + , "setText" : ["padID", "text"] + , "getHTML" : ["padID", "rev"] + , "setHTML" : ["padID", "html"] + , "getAttributePool" : ["padID"] + , "getRevisionsCount" : ["padID"] + , "getRevisionChangeset" : ["padID", "rev"] + , "getLastEdited" : ["padID"] + , "deletePad" : ["padID"] + , "copyPad" : ["sourceID", "destinationID", "force"] + , "movePad" : ["sourceID", "destinationID", "force"] + , "getReadOnlyID" : ["padID"] + , "getPadID" : ["roID"] + , "setPublicStatus" : ["padID", "publicStatus"] + , "getPublicStatus" : ["padID"] + , "setPassword" : ["padID", "password"] + , "isPasswordProtected" : ["padID"] + , "listAuthorsOfPad" : ["padID"] + , "padUsersCount" : ["padID"] + , "getAuthorName" : ["authorID"] + , "padUsers" : ["padID"] + , "sendClientsMessage" : ["padID", "msg"] + , "listAllGroups" : [] + , "checkToken" : [] + , "getChatHistory" : ["padID"] + , "getChatHistory" : ["padID", "start", "end"] + , "getChatHead" : ["padID"] + , "restoreRevision" : ["padID", "rev"] + } }; // set the latest available API version here -exports.latestApiVersion = '1.2.10'; +exports.latestApiVersion = '1.2.11'; // exports the versions so it can be used by the new Swagger endpoint exports.version = version; From 60b698f69b830b6e9e82a0d3de47763bf0c890a4 Mon Sep 17 00:00:00 2001 From: Prateek Saxena Date: Sun, 9 Nov 2014 09:18:50 +0530 Subject: [PATCH 051/455] Add keyboard shortcut for "Clear Authorshop Colors" in en locale Translate wiki should pick this up and the translations will start coming in a while. #2292 --- src/locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/locales/en.json b/src/locales/en.json index d9b983895..9a25b18cc 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -12,7 +12,7 @@ "pad.toolbar.unindent.title": "Outdent (Shift+TAB)", "pad.toolbar.undo.title": "Undo (Ctrl+Z)", "pad.toolbar.redo.title": "Redo (Ctrl+Y)", - "pad.toolbar.clearAuthorship.title": "Clear Authorship Colors", + "pad.toolbar.clearAuthorship.title": "Clear Authorship Colors (Ctrl+Shift+C)", "pad.toolbar.import_export.title": "Import/Export from/to different file formats", "pad.toolbar.timeslider.title": "Timeslider", "pad.toolbar.savedRevision.title": "Save Revision", From 9d39c9591adab5b6638e16ee54e7f87a9147e015 Mon Sep 17 00:00:00 2001 From: Cristo Date: Wed, 12 Nov 2014 19:46:50 +0100 Subject: [PATCH 052/455] update pad clients --- src/node/db/API.js | 170 +++++++++++++++++++++++---------------------- 1 file changed, 88 insertions(+), 82 deletions(-) diff --git a/src/node/db/API.js b/src/node/db/API.js index 8ad6e2eb2..b3d46ea1d 100644 --- a/src/node/db/API.js +++ b/src/node/db/API.js @@ -583,102 +583,108 @@ exports.deletePad = function(padID, callback) {code:0, message:"ok", data:null} {code: 1, message:"padID does not exist", data: null} */ -exports.restoreRevision = function(padID, rev, callback) +exports.restoreRevision = function (padID, rev, callback) { - var Changeset = require("ep_etherpad-lite/static/js/Changeset"); - - //check if rev is a number - if(rev !== undefined && typeof rev != "number") - { - //try to parse the number - if(!isNaN(parseInt(rev))) - { - rev = parseInt(rev); - } - else - { - callback(new customError("rev is not a number", "apierror")); - return; - } - } + var Changeset = require("ep_etherpad-lite/static/js/Changeset"); + var padMessage = require("ep_etherpad-lite/node/handler/PadMessageHandler.js"); - //ensure this is not a negativ number - if(rev !== undefined && rev < 0) - { - callback(new customError("rev is a negativ number","apierror")); - return; - } + //check if rev is a number + if (rev !== undefined && typeof rev != "number") + { + //try to parse the number + if (!isNaN(parseInt(rev))) + { + rev = parseInt(rev); + } + else + { + callback(new customError("rev is not a number", "apierror")); + return; + } + } - //ensure this is not a float value - if(rev !== undefined && !is_int(rev)) - { - callback(new customError("rev is a float value","apierror")); - return; - } + //ensure this is not a negativ number + if (rev !== undefined && rev < 0) + { + callback(new customError("rev is a negativ number", "apierror")); + return; + } - //get the pad - getPadSafe(padID, true, function(err, pad) - { - if(ERR(err, callback)) return; + //ensure this is not a float value + if (rev !== undefined && !is_int(rev)) + { + callback(new customError("rev is a float value", "apierror")); + return; + } + + //get the pad + getPadSafe(padID, true, function (err, pad) + { + if (ERR(err, callback)) return; - //check if this is a valid revision - if(rev > pad.getHeadRevisionNumber()) - { - callback(new customError("rev is higher than the head revision of the pad","apierror")); - return; - } + //check if this is a valid revision + if (rev > pad.getHeadRevisionNumber()) + { + callback(new customError("rev is higher than the head revision of the pad", "apierror")); + return; + } - pad.getInternalRevisionAText(rev, function(err, atext) - { - if(ERR(err, callback)) return; + pad.getInternalRevisionAText(rev, function (err, atext) + { + if (ERR(err, callback)) return; - var oldText = pad.text(); - atext.text += "\n"; - function eachAttribRun(attribs, func) - { - var attribsIter = Changeset.opIterator(attribs); - var textIndex = 0; - var newTextStart = 0; - var newTextEnd = atext.text.length; - while (attribsIter.hasNext()) - { - var op = attribsIter.next(); - var nextIndex = textIndex + op.chars; - if (!(nextIndex <= newTextStart || textIndex >= newTextEnd)) - { - func(Math.max(newTextStart, textIndex), Math.min(newTextEnd, nextIndex), op.attribs); - } - textIndex = nextIndex; - } - } + var oldText = pad.text(); + atext.text += "\n"; + function eachAttribRun(attribs, func) + { + var attribsIter = Changeset.opIterator(attribs); + var textIndex = 0; + var newTextStart = 0; + var newTextEnd = atext.text.length; + while (attribsIter.hasNext()) + { + var op = attribsIter.next(); + var nextIndex = textIndex + op.chars; + if (!(nextIndex <= newTextStart || textIndex >= newTextEnd)) + { + func(Math.max(newTextStart, textIndex), Math.min(newTextEnd, nextIndex), op.attribs); + } + textIndex = nextIndex; + } + } - // create a new changeset with a helper builder object - var builder = Changeset.builder(oldText.length); + // create a new changeset with a helper builder object + var builder = Changeset.builder(oldText.length); - // assemble each line into the builder - eachAttribRun(atext.attribs, function(start, end, attribs) - { - builder.insert(atext.text.substring(start, end), attribs); - }); + // assemble each line into the builder + eachAttribRun(atext.attribs, function (start, end, attribs) + { + builder.insert(atext.text.substring(start, end), attribs); + }); - var lastNewlinePos = oldText.lastIndexOf('\n'); - if (lastNewlinePos < 0) { - builder.remove(oldText.length-1,0); - } else { - builder.remove(lastNewlinePos, oldText.match(/\n/g).length-1); - builder.remove(oldText.length - lastNewlinePos-1,0); - } + var lastNewlinePos = oldText.lastIndexOf('\n'); + if (lastNewlinePos < 0) + { + builder.remove(oldText.length - 1, 0); + } else + { + builder.remove(lastNewlinePos, oldText.match(/\n/g).length - 1); + builder.remove(oldText.length - lastNewlinePos - 1, 0); + } - var changeset = builder.toString(); + var changeset = builder.toString(); - //append the changeset - pad.appendRevision(changeset); - // - callback(null, null); - }); + //append the changeset + pad.appendRevision(changeset); + // + padMessage.updatePadClients(pad, function () + { + }); + callback(null, null); + }); - }); + }); }; /** From 24ac082cae6ad8842d3028a14ec9ecb8bbd47b9a Mon Sep 17 00:00:00 2001 From: Cristo Date: Wed, 12 Nov 2014 19:49:08 +0100 Subject: [PATCH 053/455] Update API.js --- src/node/db/API.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/node/db/API.js b/src/node/db/API.js index b3d46ea1d..d933f99ed 100644 --- a/src/node/db/API.js +++ b/src/node/db/API.js @@ -587,7 +587,6 @@ exports.restoreRevision = function (padID, rev, callback) { var Changeset = require("ep_etherpad-lite/static/js/Changeset"); var padMessage = require("ep_etherpad-lite/node/handler/PadMessageHandler.js"); - //check if rev is a number if (rev !== undefined && typeof rev != "number") { From f59238fe58ac1d1a28896642a86f8483a0d524a3 Mon Sep 17 00:00:00 2001 From: Cristo Date: Wed, 12 Nov 2014 19:50:43 +0100 Subject: [PATCH 054/455] Update API.js --- src/node/db/API.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node/db/API.js b/src/node/db/API.js index d933f99ed..2ba99f6ad 100644 --- a/src/node/db/API.js +++ b/src/node/db/API.js @@ -587,6 +587,7 @@ exports.restoreRevision = function (padID, rev, callback) { var Changeset = require("ep_etherpad-lite/static/js/Changeset"); var padMessage = require("ep_etherpad-lite/node/handler/PadMessageHandler.js"); + //check if rev is a number if (rev !== undefined && typeof rev != "number") { @@ -682,7 +683,6 @@ exports.restoreRevision = function (padID, rev, callback) }); callback(null, null); }); - }); }; From f7dd756642623ade815f417de7b7b2eb880e488c Mon Sep 17 00:00:00 2001 From: Cristo Date: Wed, 12 Nov 2014 19:53:56 +0100 Subject: [PATCH 055/455] Update API.js --- src/node/db/API.js | 173 +++++++++++++++++++++++---------------------- 1 file changed, 87 insertions(+), 86 deletions(-) diff --git a/src/node/db/API.js b/src/node/db/API.js index 2ba99f6ad..07d3703c9 100644 --- a/src/node/db/API.js +++ b/src/node/db/API.js @@ -585,105 +585,106 @@ exports.deletePad = function(padID, callback) */ exports.restoreRevision = function (padID, rev, callback) { - var Changeset = require("ep_etherpad-lite/static/js/Changeset"); - var padMessage = require("ep_etherpad-lite/node/handler/PadMessageHandler.js"); + var Changeset = require("ep_etherpad-lite/static/js/Changeset"); + var padMessage = require("ep_etherpad-lite/node/handler/PadMessageHandler.js"); - //check if rev is a number - if (rev !== undefined && typeof rev != "number") - { - //try to parse the number - if (!isNaN(parseInt(rev))) - { - rev = parseInt(rev); - } - else - { - callback(new customError("rev is not a number", "apierror")); - return; - } - } + //check if rev is a number + if (rev !== undefined && typeof rev != "number") + { + //try to parse the number + if (!isNaN(parseInt(rev))) + { + rev = parseInt(rev); + } + else + { + callback(new customError("rev is not a number", "apierror")); + return; + } + } - //ensure this is not a negativ number - if (rev !== undefined && rev < 0) - { - callback(new customError("rev is a negativ number", "apierror")); - return; - } + //ensure this is not a negativ number + if (rev !== undefined && rev < 0) + { + callback(new customError("rev is a negativ number", "apierror")); + return; + } - //ensure this is not a float value - if (rev !== undefined && !is_int(rev)) - { - callback(new customError("rev is a float value", "apierror")); - return; - } + //ensure this is not a float value + if (rev !== undefined && !is_int(rev)) + { + callback(new customError("rev is a float value", "apierror")); + return; + } - //get the pad - getPadSafe(padID, true, function (err, pad) - { - if (ERR(err, callback)) return; + //get the pad + getPadSafe(padID, true, function (err, pad) + { + if (ERR(err, callback)) return; - //check if this is a valid revision - if (rev > pad.getHeadRevisionNumber()) - { - callback(new customError("rev is higher than the head revision of the pad", "apierror")); - return; - } + //check if this is a valid revision + if (rev > pad.getHeadRevisionNumber()) + { + callback(new customError("rev is higher than the head revision of the pad", "apierror")); + return; + } - pad.getInternalRevisionAText(rev, function (err, atext) - { - if (ERR(err, callback)) return; + pad.getInternalRevisionAText(rev, function (err, atext) + { + if (ERR(err, callback)) return; - var oldText = pad.text(); - atext.text += "\n"; - function eachAttribRun(attribs, func) - { - var attribsIter = Changeset.opIterator(attribs); - var textIndex = 0; - var newTextStart = 0; - var newTextEnd = atext.text.length; - while (attribsIter.hasNext()) - { - var op = attribsIter.next(); - var nextIndex = textIndex + op.chars; - if (!(nextIndex <= newTextStart || textIndex >= newTextEnd)) - { - func(Math.max(newTextStart, textIndex), Math.min(newTextEnd, nextIndex), op.attribs); - } - textIndex = nextIndex; - } - } + var oldText = pad.text(); + atext.text += "\n"; + function eachAttribRun(attribs, func) + { + var attribsIter = Changeset.opIterator(attribs); + var textIndex = 0; + var newTextStart = 0; + var newTextEnd = atext.text.length; + while (attribsIter.hasNext()) + { + var op = attribsIter.next(); + var nextIndex = textIndex + op.chars; + if (!(nextIndex <= newTextStart || textIndex >= newTextEnd)) + { + func(Math.max(newTextStart, textIndex), Math.min(newTextEnd, nextIndex), op.attribs); + } + textIndex = nextIndex; + } + } - // create a new changeset with a helper builder object - var builder = Changeset.builder(oldText.length); + // create a new changeset with a helper builder object + var builder = Changeset.builder(oldText.length); - // assemble each line into the builder - eachAttribRun(atext.attribs, function (start, end, attribs) - { - builder.insert(atext.text.substring(start, end), attribs); - }); + // assemble each line into the builder + eachAttribRun(atext.attribs, function (start, end, attribs) + { + builder.insert(atext.text.substring(start, end), attribs); + }); - var lastNewlinePos = oldText.lastIndexOf('\n'); - if (lastNewlinePos < 0) - { - builder.remove(oldText.length - 1, 0); - } else - { - builder.remove(lastNewlinePos, oldText.match(/\n/g).length - 1); - builder.remove(oldText.length - lastNewlinePos - 1, 0); - } + var lastNewlinePos = oldText.lastIndexOf('\n'); + if (lastNewlinePos < 0) + { + builder.remove(oldText.length - 1, 0); + } else + { + builder.remove(lastNewlinePos, oldText.match(/\n/g).length - 1); + builder.remove(oldText.length - lastNewlinePos - 1, 0); + } - var changeset = builder.toString(); + var changeset = builder.toString(); - //append the changeset - pad.appendRevision(changeset); - // - padMessage.updatePadClients(pad, function () - { - }); - callback(null, null); - }); - }); + //append the changeset + pad.appendRevision(changeset); + // + padMessage.updatePadClients(pad, function () + { + }); + callback(null, null); + }); + + }); }; /** From 0253156dbb2e26a1a894a414c61ff8e521b3fece Mon Sep 17 00:00:00 2001 From: Cristo Date: Wed, 12 Nov 2014 19:55:37 +0100 Subject: [PATCH 056/455] Update APIHandler.js --- src/node/handler/APIHandler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node/handler/APIHandler.js b/src/node/handler/APIHandler.js index 76af4aa1e..9adc24180 100644 --- a/src/node/handler/APIHandler.js +++ b/src/node/handler/APIHandler.js @@ -345,7 +345,7 @@ var version = , "getChatHistory" : ["padID", "start", "end"] , "getChatHead" : ["padID"] } - , "1.2.11": +, "1.2.11": { "createGroup" : [] , "createGroupIfNotExistsFor" : ["groupMapper"] , "deleteGroup" : ["groupID"] From e5d77c3763d28baa26b07d9cc7a1bdc59384f7f0 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 12 Nov 2014 21:39:03 +0000 Subject: [PATCH 057/455] indentation --- src/static/js/pluginfw/installer.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/static/js/pluginfw/installer.js b/src/static/js/pluginfw/installer.js index c1a9adad2..bf779d7ac 100644 --- a/src/static/js/pluginfw/installer.js +++ b/src/static/js/pluginfw/installer.js @@ -67,9 +67,9 @@ exports.getAvailablePlugins = function(maxCacheAge, cb) { return cb && cb(null, exports.availablePlugins) } plugins = JSON.parse(plugins); - exports.availablePlugins = plugins; - cacheTimestamp = Math.round(+new Date/1000); - cb && cb(null, plugins) + exports.availablePlugins = plugins; + cacheTimestamp = Math.round(+new Date/1000); + cb && cb(null, plugins) }); }; From e8fda27ead8bb550b027b9578199e2b034e388d1 Mon Sep 17 00:00:00 2001 From: Siebrand Mazeland Date: Fri, 14 Nov 2014 13:42:39 +0100 Subject: [PATCH 058/455] Localisation updates from https://translatewiki.net. --- src/locales/de.json | 6 +++--- src/locales/it.json | 9 +++++---- src/locales/lb.json | 4 ++-- src/locales/mk.json | 6 +++--- src/locales/sv.json | 14 +++++++------- src/locales/vi.json | 9 +++++---- src/locales/zh-hant.json | 8 ++++---- 7 files changed, 29 insertions(+), 27 deletions(-) diff --git a/src/locales/de.json b/src/locales/de.json index 05a9af799..25594da19 100644 --- a/src/locales/de.json +++ b/src/locales/de.json @@ -12,9 +12,9 @@ "pad.toolbar.bold.title": "Fett (Strg-B)", "pad.toolbar.italic.title": "Kursiv (Strg-I)", "pad.toolbar.underline.title": "Unterstrichen (Strg-U)", - "pad.toolbar.strikethrough.title": "Durchgestrichen", - "pad.toolbar.ol.title": "Nummerierte Liste", - "pad.toolbar.ul.title": "Ungeordnete Liste", + "pad.toolbar.strikethrough.title": "Durchgestrichen (Strg+5)", + "pad.toolbar.ol.title": "Nummerierte Liste (Strg+Shift+N)", + "pad.toolbar.ul.title": "Ungeordnete Liste (Strg+Shift+L)", "pad.toolbar.indent.title": "Einrücken (TAB)", "pad.toolbar.unindent.title": "Ausrücken (Shift+TAB)", "pad.toolbar.undo.title": "Rückgängig (Strg-Z)", diff --git a/src/locales/it.json b/src/locales/it.json index 8292c4f27..501733f8a 100644 --- a/src/locales/it.json +++ b/src/locales/it.json @@ -4,7 +4,8 @@ "Beta16", "Gianfranco", "Muxator", - "Vituzzu" + "Vituzzu", + "Macofe" ] }, "index.newPad": "Nuovo Pad", @@ -12,9 +13,9 @@ "pad.toolbar.bold.title": "Grassetto (Ctrl-B)", "pad.toolbar.italic.title": "Corsivo (Ctrl-I)", "pad.toolbar.underline.title": "Sottolineato (Ctrl-U)", - "pad.toolbar.strikethrough.title": "Barrato", - "pad.toolbar.ol.title": "Elenco numerato", - "pad.toolbar.ul.title": "Elenco puntato", + "pad.toolbar.strikethrough.title": "Barrato (Ctrl+5)", + "pad.toolbar.ol.title": "Elenco numerato (Ctrl+Shift+N)", + "pad.toolbar.ul.title": "Elenco puntato (Ctrl+Shift+L)", "pad.toolbar.indent.title": "Rientro (TAB)", "pad.toolbar.unindent.title": "Riduci rientro (Shift+TAB)", "pad.toolbar.undo.title": "Annulla (Ctrl-Z)", diff --git a/src/locales/lb.json b/src/locales/lb.json index 7f936906f..841add75f 100644 --- a/src/locales/lb.json +++ b/src/locales/lb.json @@ -6,8 +6,8 @@ ] }, "index.newPad": "Neie Pad", - "pad.toolbar.ol.title": "Numeréiert Lëscht", - "pad.toolbar.ul.title": "Net-numeréiert Lëscht", + "pad.toolbar.ol.title": "Numeréiert Lëscht (Ctrl+Shift+N)", + "pad.toolbar.ul.title": "Net-numeréiert Lëscht (Ctrl+Shift+L)", "pad.toolbar.undo.title": "Réckgängeg (Ctrl-Z)", "pad.toolbar.redo.title": "Widderhuelen (Ctrl-Y)", "pad.toolbar.savedRevision.title": "Versioun späicheren", diff --git a/src/locales/mk.json b/src/locales/mk.json index 8aaf9917b..aec40b278 100644 --- a/src/locales/mk.json +++ b/src/locales/mk.json @@ -10,9 +10,9 @@ "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)", diff --git a/src/locales/sv.json b/src/locales/sv.json index 623def9ac..ab6f3820d 100644 --- a/src/locales/sv.json +++ b/src/locales/sv.json @@ -11,9 +11,9 @@ "pad.toolbar.bold.title": "Fet (Ctrl-B)", "pad.toolbar.italic.title": "Kursiv (Ctrl-I)", "pad.toolbar.underline.title": "Understruken (Ctrl-U)", - "pad.toolbar.strikethrough.title": "Genomstruken", - "pad.toolbar.ol.title": "Numrerad lista", - "pad.toolbar.ul.title": "Osorterad lista", + "pad.toolbar.strikethrough.title": "Genomstruken (Ctrl+5)", + "pad.toolbar.ol.title": "Numrerad lista (Ctrl+Shift+N)", + "pad.toolbar.ul.title": "Onumrerad lista (Ctrl+Shift+L)", "pad.toolbar.indent.title": "Öka indrag (TABB)", "pad.toolbar.unindent.title": "Minska indrag (Shift+TABB)", "pad.toolbar.undo.title": "Ångra (Ctrl-Z)", @@ -52,7 +52,7 @@ "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.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...", "pad.modals.forcereconnect": "Tvinga återanslutning", @@ -60,7 +60,7 @@ "pad.modals.userdup.explanation": "Detta block verkar vara öppet i mer än ett fönster på denna dator.", "pad.modals.userdup.advice": "Återanslut för att använda detta fönster istället.", "pad.modals.unauth": "Inte godkänd", - "pad.modals.unauth.explanation": "Din behörighet ändrades medan du visar denna sida. Försök att återansluta.", + "pad.modals.unauth.explanation": "Din behörighet ändrades medan du visade denna sida. Försök att återansluta.", "pad.modals.looping.explanation": "Kommunikationsproblem med synkroniseringsservern har uppstått.", "pad.modals.looping.cause": "Kanske du är ansluten via en inkompatibel brandvägg eller proxy.", "pad.modals.initsocketfail": "Servern kan inte nås.", @@ -69,7 +69,7 @@ "pad.modals.slowcommit.explanation": "Servern svarar inte.", "pad.modals.slowcommit.cause": "Detta kan bero på problem med nätverksanslutningen.", "pad.modals.badChangeset.explanation": "En redigering som du gjort klassificerades som otillåten av synkroniseringsservern.", - "pad.modals.badChangeset.cause": "Detta kan bero på en felaktig konfiguration av servern eller något annat oväntad beteende. Var god kontakta tjänstadministratören om du anser att detta är ett fel. Försök ansluta igen för att fortsätta redigera.", + "pad.modals.badChangeset.cause": "Detta kan bero på en felaktig konfiguration av servern eller något annat oväntad beteende. Var god kontakta tjänsteadministratören om du upplever att detta är ett fel. Försök att ansluta igen för att fortsätta redigera.", "pad.modals.corruptPad.explanation": "Blocket du försöker komma åt är skadat.", "pad.modals.corruptPad.cause": "Detta kan bero på en felaktig konfiguration av servern eller något annat oväntad beteende. Var god kontakta tjänstadministratören.", "pad.modals.deleted": "Raderad.", @@ -84,7 +84,7 @@ "pad.chat": "Chatt", "pad.chat.title": "Öppna chatten för detta block.", "pad.chat.loadmessages": "Läs in fler meddelanden", - "timeslider.pageTitle": "Tidsreglage för {{appTitle}}", + "timeslider.pageTitle": "{{appTitle}} tidsreglage", "timeslider.toolbar.returnbutton": "Återvänd till blocket", "timeslider.toolbar.authors": "Författare:", "timeslider.toolbar.authorsList": "Inga författare", diff --git a/src/locales/vi.json b/src/locales/vi.json index 608e9645a..3d5cb66d2 100644 --- a/src/locales/vi.json +++ b/src/locales/vi.json @@ -3,7 +3,8 @@ "authors": [ "Baonguyen21022003", "Minh Nguyen", - "Tuankiet65" + "Tuankiet65", + "Max20091" ] }, "index.newPad": "Tạo một Pad mới", @@ -11,9 +12,9 @@ "pad.toolbar.bold.title": "In đậm (Ctrl-B)", "pad.toolbar.italic.title": "In nghiêng (Ctrl-I)", "pad.toolbar.underline.title": "Gạch chân (Ctrl-U)", - "pad.toolbar.strikethrough.title": "Gạch ngang", - "pad.toolbar.ol.title": "Danh sách Có Đánh số", - "pad.toolbar.ul.title": "Danh sách Không Đánh số", + "pad.toolbar.strikethrough.title": "Gạch ngang (Ctrl+5)", + "pad.toolbar.ol.title": "Danh sách Có Đánh số (Ctrl+Shift+N)", + "pad.toolbar.ul.title": "Danh sách Không Đánh số (Ctrl+Shift+L)", "pad.toolbar.indent.title": "Tăng lề (TAB)", "pad.toolbar.unindent.title": "Giảm lề (Shift+TAB)", "pad.toolbar.undo.title": "Hoàn tác (Ctrl-Z)", diff --git a/src/locales/zh-hant.json b/src/locales/zh-hant.json index 0707648a2..3b084e3d0 100644 --- a/src/locales/zh-hant.json +++ b/src/locales/zh-hant.json @@ -14,9 +14,9 @@ "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)", @@ -94,7 +94,7 @@ "timeslider.toolbar.exportlink.title": "匯出", "timeslider.exportCurrent": "匯出當前版本為:", "timeslider.version": "版本{{version}}", - "timeslider.saved": "{{year}}年{{month}}月{{day}}日儲存", + "timeslider.saved": "{{year}}年{{month}}{{day}}日儲存", "timeslider.dateformat": "{{year}}年{{month}}月{{day}}日 {{hours}}:{{minutes}}:{{seconds}}", "timeslider.month.january": "1月", "timeslider.month.february": "2月", From d7a181be677362dd993c91e6f57fc51803439cb9 Mon Sep 17 00:00:00 2001 From: Simon Gaeremynck Date: Fri, 24 May 2013 09:12:51 +0100 Subject: [PATCH 059/455] Nooping console.foo in html10n.js when the method is not defined. --- src/static/js/html10n.js | 48 +++++++++++++++------------------------- 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/src/static/js/html10n.js b/src/static/js/html10n.js index 49a0a80dc..19cf9de18 100644 --- a/src/static/js/html10n.js +++ b/src/static/js/html10n.js @@ -23,28 +23,16 @@ window.html10n = (function(window, document, undefined) { // fix console - var console = window.console - function interceptConsole(method){ - if (!console) return function() {} - - var original = console[method] - - // do sneaky stuff - if (original.bind){ - // Do this for normal browsers - return original.bind(console) - }else{ - return function() { - // Do this for IE - var message = Array.prototype.slice.apply(arguments).join(' ') - original(message) - } + (function() { + var noop = function() {}; + var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"]; + var console = (window.console = window.console || {}); + for (var i = 0; i < names.length; ++i) { + if (!console[names[i]]) { + console[names[i]] = noop; } - } - var consoleLog = interceptConsole('log') - , consoleWarn = interceptConsole('warn') - , consoleError = interceptConsole('warn') - + } + }()); // fix Array#forEach in IE // taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach @@ -148,7 +136,7 @@ window.html10n = (function(window, document, undefined) { for (var i=0, n=this.resources.length; i < n; i++) { this.fetch(this.resources[i], lang, function(e) { reqs++; - if(e) consoleWarn(e) + if(e) console.warn(e) if (reqs < n) return;// Call back once all reqs are completed cb && cb() @@ -647,7 +635,7 @@ window.html10n = (function(window, document, undefined) { // return a function that gives the plural form name for a given integer var index = locales2rules[lang.replace(/-.*$/, '')]; if (!(index in pluralRules)) { - consoleWarn('plural form unknown for [' + lang + ']'); + console.warn('plural form unknown for [' + lang + ']'); return function() { return 'other'; }; } return pluralRules[index]; @@ -727,7 +715,7 @@ window.html10n = (function(window, document, undefined) { var i = 0 , n = list.length iterator(list[i], i, function each(err) { - if(err) consoleLog(err) + if(err) console.error(err) i++ if (i < n) return iterator(list[i],i, each); cb() @@ -750,8 +738,8 @@ window.html10n = (function(window, document, undefined) { html10n.get = function(id, args) { var translations = html10n.translations - if(!translations) return consoleWarn('No translations available (yet)') - if(!translations[id]) return consoleWarn('Could not find string '+id) + if(!translations) return console.warn('No translations available (yet)') + if(!translations[id]) return console.warn('Could not find string '+id) // apply macros var str = translations[id] @@ -781,7 +769,7 @@ window.html10n = (function(window, document, undefined) { } else if (arg in translations) { sub = translations[arg] } else { - consoleWarn('Could not find argument {{' + arg + '}}') + console.warn('Could not find argument {{' + arg + '}}') return str } @@ -840,7 +828,7 @@ window.html10n = (function(window, document, undefined) { str.id = node.getAttribute('data-l10n-id') if (!str.id) return - if(!translations[str.id]) return consoleWarn('Couldn\'t find translation key '+str.id) + if(!translations[str.id]) return console.warn('Couldn\'t find translation key '+str.id) // get args if(window.JSON) { @@ -849,7 +837,7 @@ window.html10n = (function(window, document, undefined) { try{ str.args = eval(node.getAttribute('data-l10n-args')) }catch(e) { - consoleWarn('Couldn\'t parse args for '+str.id) + console.warn('Couldn\'t parse args for '+str.id) } } @@ -887,7 +875,7 @@ window.html10n = (function(window, document, undefined) { } } if (!found) { - consoleWarn('Unexpected error: could not translate element content for key '+str.id, node) + console.warn('Unexpected error: could not translate element content for key '+str.id, node) } } } From 020b636b1f6ffecfd6cf5563e0867ce4c401aebe Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 18 Nov 2014 14:08:44 +0000 Subject: [PATCH 060/455] template of a semi-fix --- src/node/hooks/express/adminplugins.js | 6 +++--- src/static/js/admin/plugins.js | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/node/hooks/express/adminplugins.js b/src/node/hooks/express/adminplugins.js index d8f19bba9..b348c0233 100644 --- a/src/node/hooks/express/adminplugins.js +++ b/src/node/hooks/express/adminplugins.js @@ -14,7 +14,6 @@ exports.expressCreateServer = function (hook_name, args, cb) { search_results: {}, errors: [], }; - res.send( eejs.require("ep_etherpad-lite/templates/admin/plugins.html", render_args) ); }); args.app.get('/admin/plugins/info', function(req, res) { @@ -25,7 +24,8 @@ exports.expressCreateServer = function (hook_name, args, cb) { exports.socketio = function (hook_name, args, cb) { var io = args.io.of("/pluginfw/installer"); io.on('connection', function (socket) { - if (!socket.handshake.session.user || !socket.handshake.session.user.is_admin) return; + console.warn("THIS IS BROKEN", socket.handshake); + if (!socket.handshake.session || !socket.handshake.session.user || !socket.handshake.session.user.is_admin) return; socket.on("getInstalled", function (query) { // send currently installed plugins @@ -107,4 +107,4 @@ function sortPluginList(plugins, property, /*ASC?*/dir) { // a must be equal to b return 0; }) -} \ No newline at end of file +} diff --git a/src/static/js/admin/plugins.js b/src/static/js/admin/plugins.js index e6c7a1224..8a4c732e5 100644 --- a/src/static/js/admin/plugins.js +++ b/src/static/js/admin/plugins.js @@ -10,7 +10,8 @@ $(document).ready(function () { resource = baseURL.substring(1) + "socket.io"; //connect - socket = io.connect(url, {resource : resource}).of("/pluginfw/installer"); + var room = url + "pluginfw/installer"; + socket = io.connect(room, {resource : resource}); function search(searchTerm, limit) { if(search.searchTerm != searchTerm) { From d10013c1590594303397476f125354aa945ac483 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 18 Nov 2014 14:10:38 +0000 Subject: [PATCH 061/455] client side code now fixed --- src/static/js/admin/settings.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/static/js/admin/settings.js b/src/static/js/admin/settings.js index fb3f2e973..8a4473d6a 100644 --- a/src/static/js/admin/settings.js +++ b/src/static/js/admin/settings.js @@ -9,7 +9,8 @@ $(document).ready(function () { resource = baseURL.substring(1) + "socket.io"; //connect - socket = io.connect(url, {resource : resource}).of("/settings"); + var room = url + "settings"; + socket = io.connect(room, {resource : resource}); socket.on('settings', function (settings) { From ff603d7b586f4a8d675e30d8ff659f5c189afd5a Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 18 Nov 2014 14:12:02 +0000 Subject: [PATCH 062/455] stability restored now to handle the auth issues --- src/node/hooks/express/adminsettings.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/node/hooks/express/adminsettings.js b/src/node/hooks/express/adminsettings.js index 2a48d2899..ba3009cd9 100644 --- a/src/node/hooks/express/adminsettings.js +++ b/src/node/hooks/express/adminsettings.js @@ -22,7 +22,9 @@ exports.expressCreateServer = function (hook_name, args, cb) { exports.socketio = function (hook_name, args, cb) { var io = args.io.of("/settings"); io.on('connection', function (socket) { - if (!socket.handshake.session.user || !socket.handshake.session.user.is_admin) return; + console.warn ("THIS IS BROKEN"); + + if (!socket.handshake.session || !socket.handshake.session.user || !socket.handshake.session.user.is_admin) return; socket.on("load", function (query) { fs.readFile('settings.json', 'utf8', function (err,data) { From 3e8f3cd938995a75e5146a149bf339dd5603416f Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 18 Nov 2014 14:56:40 +0000 Subject: [PATCH 063/455] hrm I dont trust this security --- src/node/hooks/express/adminplugins.js | 5 +++-- src/node/hooks/express/adminsettings.js | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/node/hooks/express/adminplugins.js b/src/node/hooks/express/adminplugins.js index b348c0233..ccc100dcc 100644 --- a/src/node/hooks/express/adminplugins.js +++ b/src/node/hooks/express/adminplugins.js @@ -24,8 +24,9 @@ exports.expressCreateServer = function (hook_name, args, cb) { exports.socketio = function (hook_name, args, cb) { var io = args.io.of("/pluginfw/installer"); io.on('connection', function (socket) { - console.warn("THIS IS BROKEN", socket.handshake); - if (!socket.handshake.session || !socket.handshake.session.user || !socket.handshake.session.user.is_admin) return; + + 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; socket.on("getInstalled", function (query) { // send currently installed plugins diff --git a/src/node/hooks/express/adminsettings.js b/src/node/hooks/express/adminsettings.js index ba3009cd9..3529e2059 100644 --- a/src/node/hooks/express/adminsettings.js +++ b/src/node/hooks/express/adminsettings.js @@ -22,9 +22,9 @@ exports.expressCreateServer = function (hook_name, args, cb) { exports.socketio = function (hook_name, args, cb) { var io = args.io.of("/settings"); io.on('connection', function (socket) { - console.warn ("THIS IS BROKEN"); - if (!socket.handshake.session || !socket.handshake.session.user || !socket.handshake.session.user.is_admin) return; + 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; socket.on("load", function (query) { fs.readFile('settings.json', 'utf8', function (err,data) { From c14e1696b312610fdcfecf7f5cba0de957e4f45a Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 18 Nov 2014 18:09:29 +0000 Subject: [PATCH 064/455] remove stuff, new files --- src/static/css/pad.css | 5 +- src/static/font/fontawesome-etherpad.eot | Bin 10200 -> 15442 bytes src/static/font/fontawesome-etherpad.svg | 93 ++++++++++++------ src/static/font/fontawesome-etherpad.ttf | Bin 9984 -> 15224 bytes src/static/font/fontawesome-etherpad.woff | Bin 6188 -> 10076 bytes src/static/img/backgrad.gif | Bin 697 -> 0 bytes src/static/img/connectingbar.gif | Bin 10932 -> 0 bytes src/static/img/crushed_button_depressed.png | Bin 4134 -> 0 bytes src/static/img/crushed_button_undepressed.png | Bin 4166 -> 0 bytes src/static/img/crushed_current_location.png | Bin 1009 -> 0 bytes src/static/img/etherpad_lite_icons.png | Bin 8318 -> 0 bytes src/static/img/fileicons.gif | Bin 1649 -> 0 bytes src/static/img/gritter.png | Bin 4880 -> 0 bytes src/static/img/leftarrow.png | Bin 494 -> 0 bytes src/static/img/loading.gif | Bin 658 -> 0 bytes src/static/img/pause.png | Bin 2883 -> 0 bytes src/static/img/play.png | Bin 3017 -> 0 bytes src/static/img/roundcorner_left.gif | Bin 123 -> 0 bytes src/static/img/roundcorner_right.gif | Bin 131 -> 0 bytes src/static/img/star.png | Bin 3241 -> 0 bytes src/static/img/stepper_buttons.png | Bin 4858 -> 0 bytes src/static/img/timeslider_background.png | Bin 182 -> 0 bytes src/static/img/timeslider_left.png | Bin 686 -> 0 bytes src/static/img/timeslider_right.png | Bin 517 -> 0 bytes src/static/js/pad_impexp.js | 26 +---- src/templates/pad.html | 1 - 26 files changed, 68 insertions(+), 57 deletions(-) delete mode 100644 src/static/img/backgrad.gif delete mode 100644 src/static/img/connectingbar.gif delete mode 100644 src/static/img/crushed_button_depressed.png delete mode 100644 src/static/img/crushed_button_undepressed.png delete mode 100644 src/static/img/crushed_current_location.png delete mode 100644 src/static/img/etherpad_lite_icons.png delete mode 100644 src/static/img/fileicons.gif delete mode 100644 src/static/img/gritter.png delete mode 100644 src/static/img/leftarrow.png delete mode 100644 src/static/img/loading.gif delete mode 100644 src/static/img/pause.png delete mode 100644 src/static/img/play.png delete mode 100644 src/static/img/roundcorner_left.gif delete mode 100644 src/static/img/roundcorner_right.gif delete mode 100644 src/static/img/star.png delete mode 100644 src/static/img/stepper_buttons.png delete mode 100644 src/static/img/timeslider_background.png delete mode 100644 src/static/img/timeslider_left.png delete mode 100644 src/static/img/timeslider_right.png diff --git a/src/static/css/pad.css b/src/static/css/pad.css index 91c97c5e1..55a24733a 100644 --- a/src/static/css/pad.css +++ b/src/static/css/pad.css @@ -637,12 +637,15 @@ table#otheruserstable { /* hidden element */ #importstatusball, -#importarrow, #importmessagesuccess, #importmessageabiword { display: none; } +.throbbold{ + font-weight:bold; +} + #importmessageabiword { color: #900; font-size: small; diff --git a/src/static/font/fontawesome-etherpad.eot b/src/static/font/fontawesome-etherpad.eot index 9a24fc67b57e3a10914eaf44d36b752b6985af05..cc934ce676862037a6df6a17bf934512bb965083 100644 GIT binary patch literal 15442 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 R&9BjGAJ>IXAv -Copyright (C) 2014 by original authors @ fontello.com +Generated by Fontastic.me - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/static/font/fontawesome-etherpad.ttf b/src/static/font/fontawesome-etherpad.ttf index a179bd191b4083b045254772eb892f768a3779bd..bbfd72170107b1eeb77dbef5373126cd071f6734 100644 GIT binary patch literal 15224 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 literal 9984 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 aIa@bOP`AbY|KEcWh_UFl?<5|8@P7btTjlux literal 6188 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

g!zN~?zf@OJ<|ib$2bX&K&R1jv2?Wo4 zQO?Gh!bz1KPt4br->FYaDCqKw7tV0C_l7HIO`2qoVw`;23$jIoS7&$*Q)nJBdp|fa zL|Q3%syk?Y<8Sw@BADI&JM#zu^kUWF|9dwcNg6&;0z`^Sj8Kms_YpE^aU_FSH6XY} z3g^yR+>mLx*g0QUmzm*lM*bgJl`@?Vv9T|sC}`Z96yl@_ItHcbFJK&?UG#>aK`9w; z5#NI}|L3AhWE|3p&uFY`gO0-FmOdbiu{ za=9E1hsWaqLC|0@Xti1dL2Nc#AQ15T{SXAn)K7z%}yN+s|gIJOb{ubQ##?I&RO z7OtrxHzTP9?0+ENUqe<8J;bwavdzPfq2$U*C#SODR1Z=Xz6;-%po^mCg=}khz3n9u zj&4;|*N|$T0`;$6H#9aiziDZ0t510QuCuHA{fEv&ePjytBaPnwX<)E#2ZK4x8ewy| zUwC{*oKPellSs$KNmJ8bWi#Kt&w_Hr)GkmI{K;}Ig z$feJ=9L;W+0jd4+jx++J=t(;X>N-=Z=*{B|z&%;T$IWuyAY!E8NUMAte@H{m?MN?H z@IMo1@icrNUcfLg346=z3IWr^zAbA=&c9w>$1Bq@ilI@KRak+MAK`=}c1dLt<8 diff --git a/src/static/img/connectingbar.gif b/src/static/img/connectingbar.gif deleted file mode 100644 index 5e54d6946960f0389f56639ebbc5bd41ab23caaa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10932 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 diff --git a/src/static/img/crushed_button_undepressed.png b/src/static/img/crushed_button_undepressed.png deleted file mode 100644 index d86e3f39743bc7b5334c58442a000bd39f772b09..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4166 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 QNB{r;07*qoM6N<$f)X$A_5c6? diff --git a/src/static/img/crushed_current_location.png b/src/static/img/crushed_current_location.png deleted file mode 100644 index 76e08359e3cdef6b81bf1799de7d9d1333ced9e6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1009 zcmV4Tx07%D;lubw!VHn5%vyBL$L1eZG#n8bdR3?p50&~}|Y-L^6T}=^UoqczA za-5xJcGnb%5TaXI$LL~7bg+mH;h~^Y9U=tH>L5XMvIruGP=a!lflfp+G*2MVV=={I+u3*z&b%+znSNh? z@6wl(2Or&d>ig(^5$5psQ?64%fTIpj5x4n-$s;zOHF-RxrHF@3mJ*RDu|{00hk|Xy zcZv7J9X#UTC^ICWq?I@+X*%h+Du|mhQry&>Cl=MHtP-!9_NXC^Q4G!N`f2XMZZqWr zS`Gob-V|*^1~C5sI8a@*)wqHE1HeLK(Y7A6_QO>!8nMO(%fyv=si#lb?0Pj&dK=ih zmCgRTm(6b8ryA79OGQfSncR*z?k!;VYd@2IFi$(NJ(G2sfLYq(z;3{I4Y>0eIQkga z_Y4S>1EFTPt|EV>*k!9uTT+|Q<#-|`G>GCU!9%Z>3EiqxUn@iuMX+3kpfiK%85XU_ zn39%q_4d+L#?){h-Dy786=rngn46A9+G&@lC;l2`?H%TOBz_o^JG;#V(zP1XJHs|} z$K+IZ$Yxi+s`z_y>{dtoy*W)@OL>ENeIwJ?k+)xroC^?F5eG+;J~O70a$6@8p#hsY zWirv`&+$7w9W?76p_n;MvHtVao9h(@p@2b#O1~-u1knXA>YzbK5;5|~6d{w9Sur}X zfzdxkDhN1OApaWWtXVmikfv-is~=uJrfUldg7`uFF0P2P;+*(xrYc=i^i1m)WNGo{ z57IR&=XG=5Tu-*XM0GvnRS+lZSYal^(W#@mm^xEcR5EL@>;4VLl+0174kMP*s8uOr z`3(B?II8WmJ&Juj_1s;owZbn9FPFWZ+On(Uwy9WQs;$q(IA=cN5A(6$#NkC3M6+2gxWKJ~}`KYMUz8EMX|Kk@(Bqq`;~X>zMQMY^9n>Q4V> f7zLwX6p#V{MAqJ+{L9Q100000NkvXXu0mjf5AW6a diff --git a/src/static/img/etherpad_lite_icons.png b/src/static/img/etherpad_lite_icons.png deleted file mode 100644 index 27867d428d6dcdb62a1271f691094f59333c02bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8318 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 diff --git a/src/static/img/fileicons.gif b/src/static/img/fileicons.gif deleted file mode 100644 index c03b6031a62780479212fe50240ccb60dd3750b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1649 zcmV-%29EhhNk%w1VG#hQ0LFg+N=i!c@$svxtN8f%?Ck87m6fH(lWA#bk>88L!^5H0 zkZhRMh{M*(%FAi5&)DhJ@UW|TdV1ENII*#@+}zw~iqb`iz>v zf~|G_{{L2K$m;6q^z-xg+Sq8atmy3bRMTo|S1^Qxgj#dVR;1GS>F8CtoVe@D)85Wm zebCj_)SjN7e3;j!+1Po*$&9wKinoi9rQ7nuzvO&%eWq>QR88e;V~mZB=jZ3( zdUlW1tH+_Iwc_h`Z02WOZJt~9_xfah(8$`t?eF;7-}3tU`uqI;&e72{G&SI;IqlGZ z`1A9#zpu-QhV7!9-oAgUgfr;PKU`c~`}_WqawOU~EzCkajOU@7!GiFCb+Eza-*Ii` zUQ=L%(ERu8XOqyCkY>5DU+(Pa!j(Fhl8RTV-r>@!$j8Tg#dN>dy29eFdU0Rt+`#nC z%lG;GPL;ljjgelWhk&eXK{ywRxouLVWc&O3Zf41egav$kkdcrRhI|8iewmsB2b6?;dmtO50RbO=Je`6I z3uLThWEB-+ryC%u3v)}mydy>gntpwM3rjF$H9s&lEiC~AA3VauORQgFH83qF8>PU* zWX2UgEk8yzCj+(t(-lkPMn)$_NheC`8|_#XEkWc#NIdKrKyaFv4n}?yF*hQhl6wQD z8EJ^5V#OpL9zL9Q;)Dl}9zurPLlV<4L4AU$)O7NXrIo=3@EPPtlFdGR3NYHqv!~CW zK!f(Id9#lvLY6S8a!+>;I0au2I3<&N3@ivWv0xl~Y$RNSE3x{>nh$zS)1BHwXD)bm2 zg0HZG3l7wvut3L-V}cR$`e-8pg__bnhyZ~&?E!}_pdEsgf%-)g+#e|g10Z4jtpp%_0~V;#J)<<32?!eO@WKuh5MZH&gT(hj0X$HU zKmi3%AYuV8pyY=J5inr_00$Hh!2tl6kU#_~?qC8F8vvjI6A1_azy=AJprQbLxc&&h zl1$n-lS>0o5aR>_I1)ht0!&cA0fN*t!4(Ja!N&_o@^Qcw6ojYdnryD=h?sD~SwIJc zoVQd?bPmFY5);%|ihFqKNk9{XHc(0}8}M-y9|V+PMhOjcbBZQUx^#vWRzRxGGXhr1 zM*?ZM@InL-66Wa(9dtlJ6DE|(&XY=9;XY0?k_FFvJZIq}JgL zJwPyp4I%WP!wnY?D22|Mkn~UrP-Hud4Lxijhk?p12SyH3(5set^HOK;9{?A%oxg0i z-EZ2Gln13!F%|3-dkRklU%jZ&SBrfd50HQc6HqBYFbV#*smKZX!N%Z(8~zQ)DlN!P z<4iR0IOH1S(|F~mjqHL8nQP7l8$zq7iPYGRY$64t$G)8EVoO&xOlQMw)7ozL?)!JJ zColmJFT4N*5Nikk2O4P(AOH|?M4!U+)Mt?c8fc)s#`ZuIF2D@Z2_#_s6gj~0#|tR{ zA$*qKi+_Fj1N v0ZdU3Bme>lvQU7+4Wfq=KpzEOu){IBpb!E`00punN5K){iN9k+AOHY6T+#W% diff --git a/src/static/img/gritter.png b/src/static/img/gritter.png deleted file mode 100644 index 0ca3bc0a0f8068194082db9719d6e20a8645985f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4880 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 Zf3P5-;PGuo@D~?gbJgKWjfL<1{{eTAuA%?{ diff --git a/src/static/img/leftarrow.png b/src/static/img/leftarrow.png deleted file mode 100644 index 1bec1288271826468e736f7b62acda6d0f622efa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 494 zcmVEF;N?0BC*%=0f<)+3lojLfpj;s=GHkBLvw|~A)CxE z*}J{lkIVn&YlUDKhG7_nVHk#CMnZsyhqHm)5VZ$u)} zrfzH!7MU6MhV_$16cyGGFlVf5U7TO05U~je^WcI&1VL`qZ}i>)*R_70VK9c&+`$~- zucP(-@u!#P`-xhl-M)z`rN{~pYz3_obhX3!(IW}Dy|aA*7P*lnClm`~71HTE%#j=9 z0DS=;|0>|mLb~0@15~OYqzpHAhn{|=OiyD${IwA6d07hF0(IYw77~vy^peR$ad~NR zRtxQCsaG>yYlw7e<;?TEgeD8=f40!bbtIEn>(Q;)YF#zSLN)4V1E%^y9b*oSjr!?& zzq->wak#P@cWB()%D44b6?cq@ksx+C>kvQ>N_Ox9`D&2L%NMD^{$ycJ12Rw{QRd|IdI8DE{a6 za}5c0b_{Se(lcOY1gcT|$-*hZz`>vcQV%kgfyGqdr043r7O&6l{=a~|=W;^ZwS|6e z-C?f|?s_kB-Ys{1(CpV8(f!MWm1-Q0a0FPX&cCptM2HcaNhS!BA{Ck>61-fz;@rDe zWnY!k7d)*u>DG6X>$dTE;#2+zEOHP`WYU>`A;W|Tn?XoUnyKLD$QkEqCHN+D>Cs!e zM0A8p)_Up8^ESKpM%?8~ozTRF6ela7lR_X)Vny?uGr~!C6#SelSv5K&-h}KCR$RU2 z;h{-B7uJ3e+bFrBjQLrM{38DL3mIeskIJRFR0<^mRA9QZj|vA!wWGg;B!)mCr^xA!K;+d?vqBEj1zxyuWxJC~S2ZTKeY+!q& zV??VrQJU=09J Cwd;@o diff --git a/src/static/img/pause.png b/src/static/img/pause.png deleted file mode 100644 index 657782c0fac9f6e6058803bd276a36b73b83d1de..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2883 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 U&g~@{0(_l^4=OOYFfdpH0MXt-F#rGn diff --git a/src/static/img/roundcorner_right.gif b/src/static/img/roundcorner_right.gif deleted file mode 100644 index 97acfbf2e6b0d7a6876999603877bbdb61736e7e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 131 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 diff --git a/src/static/img/timeslider_background.png b/src/static/img/timeslider_background.png deleted file mode 100644 index 851af4e8000a0de07f0bb2248eb73c0c817f0b17..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 182 zcmeAS@N?(olHy`uVBq!ia0vp^Oh9bU!OXzG*wuaWGms-$;u=vBoS#-wo>-L1;Fyx1 zl&avFo0y&&l$w}QS$Hzl2B?TXz$e7@-@kwV|Nl=*OFMJs%=ho#xni=90i|p`T^vIy zZY3q8B{VuKNGmi;efqJv}@+I#ZO?)Y#I} dG@OLk8TK(UUcVv{dJ$+4gQu&X%Q~loCIDe5JEZ^s diff --git a/src/static/img/timeslider_left.png b/src/static/img/timeslider_left.png deleted file mode 100644 index 48a9b0e1797c61874ac301fc2c518c3bc575b693..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 686 zcmeAS@N?(olHy`uVBq!ia0vp^Z9r_#!OXzGn3`J90pv)QxJHx&=ckpFCl;kLIHu$$ zr7C#lCZ?wbr6#6S7M@JC0V=8v@CkAK_wV22$&=I4(&FRemn~cN_3PJp^X7f{@L|P@ z6+eIeym8~k^5x6duV4TB_wQS`Zmn9iYQu&NFJ8P@vSi8m^XIQ$zkcrAxuZvqo;Y#h z-Me=`e*F0L>(|AL7oRva#N!CE=vZ+BMQ9>-DJ-emF6f(JQGlD$`rR^;80*oEOlad!TIIG!Gig# ztoRdET_Tov$*Qt2Fk3rrXJ8QkQqCN+7p~K5(f6@%(Bfc~E)5YVdwC(y(IQTx-?Gw4 zi-D=5F?Le$Tqg&YX$t3fm~IC1)+;T!!(*y7>!6myKEYKXJi%83jaTHer84BOF}FI* z;CkuY96QBn{R$=)fo8V(T1V3~PIv{q5!cjeX}6ksCgHJy^=s}<=d%wMEZ|6LoVCy_ zi|y)}2SA3`K0&56AwJ2A(|TqZ2Ax^3rB2c(Eg#0phTQN0!Mlk<>LSn-SP!miR^E*q3WToHk{**(JN!lZD$1S`>R? z+D@&l+1SnKEAjpQt^ILddQ%T}EZ%H9XCrf>@wTJIrPkfRNM-PJ^>bP0l+XkKhssVM diff --git a/src/static/img/timeslider_right.png b/src/static/img/timeslider_right.png deleted file mode 100644 index 1a1b2685b24a2e620a33d95f45ff6fe6867d20c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 517 zcmeAS@N?(olHy`uVBq!ia0vp^vw_&2gPDPWal2{N1t3SV#5JNMI6tkVJh3R1!7(L2 zDOJHUH!(dmC^a#qvhZZ84N#F^fKQ0)@87@w{{8#o$B&;sf2O6SefaR z@80?I=dWMC{`KqEix)4xeEIU%uV0@(e}4Dw-K|@<&Ye5=@87>ur%v6zef$6a|7Xse z`TqSokg;8a>pak8sgfYSU?3MeU=UGaxCL~px~Gd{NX4xrf#WP|c!VMtd|8fmhH?rx z@I>4>>98b?O(|C`il@7hfwAT4#b&R$7Nu7^yMwRR0{OaihK&6x6Q30dt@Nu2k)3;` zbMn%@nSSq***O%FE^8V#%zLsC?!d<{lIHwaQP_OgqVUgD0pZSLhDJs~$2Ddx zu`bkNit}t>ShTFLX;R1+ZQ;cnq0W8%yFMCZ-CB{kcv_bUT3g62(&S+u-Lk3y2GiT|Lvn#Y}u76H&*X|u)%QKOlHHA zIfYueyDw~7sCejBh|xM<#$4?UzL|@A&2IfmEVeECIaxfKZJl)PmWyuDYtP0yZoOOo us$!eM0<(?x&oJdLY0Rl}-1@UpUwsi{&#~=IeanCW%i!ti=d#Wzp$PzhYXgh` diff --git a/src/static/js/pad_impexp.js b/src/static/js/pad_impexp.js index aa48ad779..1454bb312 100644 --- a/src/static/js/pad_impexp.js +++ b/src/static/js/pad_impexp.js @@ -35,32 +35,10 @@ var padimpexp = (function() function fileInputUpdated() { + $('#importsubmitinput').addClass('throbbold'); $('#importformfilediv').addClass('importformenabled'); $('#importsubmitinput').removeAttr('disabled'); - $('#importmessagefail').fadeOut("fast"); - $('#importarrow').show(); - $('#importarrow').animate( - { - paddingLeft: "0px" - }, 500).animate( - { - paddingLeft: "10px" - }, 150, 'swing').animate( - { - paddingLeft: "0px" - }, 150, 'swing').animate( - { - paddingLeft: "10px" - }, 150, 'swing').animate( - { - paddingLeft: "0px" - }, 150, 'swing').animate( - { - paddingLeft: "10px" - }, 150, 'swing').animate( - { - paddingLeft: "0px" - }, 150, 'swing'); + $('#importmessagefail').fadeOut('fast'); } function fileInputSubmit() diff --git a/src/templates/pad.html b/src/templates/pad.html index 15fd45e2d..83d09f88b 100644 --- a/src/templates/pad.html +++ b/src/templates/pad.html @@ -192,7 +192,6 @@ -

From 21ce9b4dd38ef7cbe76ad2a3c3cb2ea2909d8148 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 18 Nov 2014 18:44:45 +0000 Subject: [PATCH 065/455] 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 13f28f2bf..f129e86d3 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 55a24733a..f97e1b3e1 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 066/455] 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 000000000..34231cca5 --- /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 067/455] 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 f97e1b3e1..d30d33034 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 75c47ad49..db90ee040 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 83d09f88b..f4107f2cf 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 068/455] 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 e1c199989..4f5b23c7c 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 f4107f2cf..aba3f64f7 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 069/455] 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 000000000..3bbfe6e9a --- /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 070/455] 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 19f8dde07..fceb894f5 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 071/455] 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 bf779d7ac..90bd9aa27 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 072/455] 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 6608df3e6..a45a79377 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 073/455] 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 a45a79377..468d8f649 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 0b0147dd6..822526a38 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 074/455] 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 468d8f649..f5bbb8be5 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 075/455] 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 3bbfe6e9a..000000000 --- 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 076/455] 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 f5bbb8be5..09db6dc82 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 077/455] 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 d39bfecd7..bdf2d5569 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 078/455] 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 15fd45e2d..ed1332a7c 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 70001f8f2..05d764a79 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 850d25168..3728efb2b 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 9ac8ca3dc..191d802e0 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 000000000..f763963e1 --- /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 146ec51b5..c59c36a68 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 114c4d727..8a61627d2 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 0d61e0ddd..000000000 --- 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 4f5b23c7c..78c206570 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 93c8ff708..595de92a2 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 d9d1ed163..1c0d98e64 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 f02670022..c20530ef8 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 fceb894f5..1fab02796 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 089/455] 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 3728efb2b..c9991a9a1 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 090/455] 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 3529e2059..42a07c73e 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 091/455] 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 ccc100dcc..ded1f6601 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 092/455] 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 6c029325f..598b2df54 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 093/455] 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 4dea73961..000000000 --- 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 094/455] 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 c8ecd71e4..000000000 --- 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 e940f8e0f..000000000 --- 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 ddcd0572d..000000000 --- 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 095/455] 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 48188dfd0..79217cb65 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 4b0ae82b4..e69d97305 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 5e393670f..048ed4915 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 096/455] 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 8c410ecde..22d7a761b 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 097/455] 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 22d7a761b..5cef08292 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 098/455] 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 e88a3f4c0..adf152124 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 099/455] mocha --- src/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/package.json b/src/package.json index 4b0ae82b4..4a472be07 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 100/455] 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 5cef08292..55915d760 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 101/455] 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 79217cb65..34e9ad76a 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 102/455] 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 000000000..ab07e012b --- /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 000000000..5cd567c65 --- /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 103/455] mocha version --- src/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/package.json b/src/package.json index 4a472be07..5f684d0ee 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 104/455] 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 5cd567c65..685714c16 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 105/455] 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 a8bc88a87..58eb32f2d 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 92ae8d482..f21623101 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 34e9ad76a..59802f9bf 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 106/455] 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 9adc24180..a26dd2cfb 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 685714c16..40aa2183f 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 107/455] 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 40aa2183f..d485c900f 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 108/455] 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 d485c900f..279f3088a 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 109/455] 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 ab07e012b..ec12775ba 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 279f3088a..efb3f6dee 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 000000000..921724c7d --- /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 110/455] 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 efb3f6dee..fa9863fb8 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 111/455] 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 921724c7d..41c16b4e1 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 112/455] 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 41c16b4e1..77e8b25d2 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 113/455] 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 77e8b25d2..583e2c36c 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 114/455] 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 583e2c36c..fc9137489 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 115/455] 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 1408d9fe5..f173b94ec 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 116/455] 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 fa9863fb8..80f77220f 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 fc9137489..86ba454a2 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 117/455] 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 c9991a9a1..ea47360b7 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 118/455] 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 048ed4915..1268e279c 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 119/455] 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 e69d97305..c6e07255b 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 120/455] 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 58d08b30e..e6b19542c 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 121/455] 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 ea47360b7..fdcd134df 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 122/455] 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 ac85a1360..1bf104952 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 3c3b7b05a..604ae1bc2 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 8f722125b..4801b95cb 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 123/455] 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 95da7331c..e023965f0 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 b33f5168c..a0cf0c3a6 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 3d14a7a8b..74e32f970 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 29dbae59c..979f922bb 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 c2f8a95a6..bb955b26a 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 0c58c9b84..31a474683 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 124/455] 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 a0cf0c3a6..c1cdc7244 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 125/455] 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 679381349..76fde33af 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 126/455] 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 e023965f0..703d0815a 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 c1cdc7244..48dd94a66 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 74e32f970..3730f6bf5 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 979f922bb..955cec0fd 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 bb955b26a..99056fb75 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 44bdd6111..ff7dace17 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 31a474683..b8b7c785b 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 127/455] 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 1851305f8..201ee4c8c 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 128/455] 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 bdf2d5569..73a968eee 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 129/455] 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 73a968eee..76a79a629 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 130/455] 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 5045f2995..8b7e82581 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 131/455] 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 000000000..d696eca79 --- /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 132/455] 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 7a8cd74ef..986256227 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 133/455] bump vs --- src/package.json | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/package.json b/src/package.json index 986256227..1e85ca530 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 134/455] 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 8b7e82581..177ce91e6 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 135/455] 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 177ce91e6..0ec2a5f55 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 136/455] 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 0ec2a5f55..57575ecad 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 137/455] 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 76a79a629..7e750a3e0 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 138/455] 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 e6b19542c..132d63dae 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 1e85ca530..3d5f5e24e 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 139/455] 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 57575ecad..2dd1663dd 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 140/455] 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 598b2df54..bc8f5d629 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 141/455] 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 8b7e82581..f59403fcf 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 142/455] 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 000000000..8ed9c5065 --- /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 143/455] Fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a28e90fbc..0cddb0b0f 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 144/455] 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 d349467b9..c83833420 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 145/455] 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 355bef4a9..366ad15f9 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 146/455] 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 bc8f5d629..1d89288cf 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 147/455] 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 366ad15f9..4d5487f82 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 148/455] 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 435872ea1..90f1b59c4 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 01920da79..16c676180 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 149/455] 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 16c676180..da2eb2b31 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 150/455] 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 da2eb2b31..bd6b160cd 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 151/455] 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 90f1b59c4..e730f18ee 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 152/455] 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 cb1ee1d50..757fac5ae 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 153/455] 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 b8a58b316..ca429a075 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 e730f18ee..69f5ab5c4 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 154/455] 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 fb2cb82ba..7ecf8e226 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 155/455] 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 78c206570..89ebfa764 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 156/455] 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 bd6b160cd..cc32ea321 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 157/455] 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 69f5ab5c4..251cbf11e 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 158/455] 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 cc32ea321..693cf9cf3 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 159/455] 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 191d802e0..a25d889b9 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 160/455] 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 4a9123682..2f6542e41 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 4670696a3..2791334bd 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 48185d80b..30f5a442f 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 c6573c800..e858b8008 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 07a9b971d..d6c5c52c7 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 1980bdf30..9774dc3f7 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 f3efc4074..fe362c4b7 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 146ec51b5..f83e6d6ce 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 ee0a1c27a..0a97498ad 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 a8ac413f6..cf5fcc4eb 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 b3c10b8a3..d00c59125 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 161/455] 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 f83e6d6ce..006b75f4d 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 162/455] 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 5ba608e98..e0f569efb 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 df3c38267..6fae57ffb 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 52a504f10..5c45ddb30 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 5bedcce22..017f76b5b 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 ded1f6601..8e372f1c0 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 42a07c73e..4986f093c 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 9a0a52bfb..d60d38633 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 adf152124..b70aa50ed 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 7d654c1b2..c6a227451 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 e8daa61ce..55706a703 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 6998853f2..b798f2c78 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 62631b937..678156596 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 5f12bd975..1d9ac5d30 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 136896f06..3297c6d36 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 f0b62743a..a6bec4a58 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 132d63dae..07b61c654 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 d30dc398b..971343561 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 e8d02dd6f..a5d30f960 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 163/455] 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 2791334bd..94049ff74 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 164/455] 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 a1e2c7570..78441ba04 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 165/455] 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 d00c59125..b3c10b8a3 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 166/455] 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 9774dc3f7..1980bdf30 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 167/455] 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 9774dc3f7..1980bdf30 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 168/455] 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 132d63dae..e6b19542c 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 3d5f5e24e..03e892d59 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 169/455] 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 757fac5ae..675f19d02 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 170/455] 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 b656d5d9b..25fbf8e87 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 171/455] 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 80f77220f..e6b6de81c 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 172/455] 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 7ecf8e226..ba066f18a 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 173/455] 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 5d3377351..4fd15b5c2 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 174/455] 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 b88db7a1d..575ee1a65 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 4fd15b5c2..f1fc11600 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 175/455] 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 693cf9cf3..85d5e7a29 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 176/455] 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 9a25b18cc..9a5fe45f1 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 5bedcce22..bc03f532d 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 f5a3e5a19..378e88656 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 f5d2d177f..000000000 --- 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 ba066f18a..c1035e8d5 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 1454bb312..20cae2a09 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 c20530ef8..2dd66aa90 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 1fab02796..a619c7021 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 177/455] 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 e6b6de81c..700c498f6 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 86ba454a2..4742852ff 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 178/455] 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 bc8f5d629..7cdefc65c 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 179/455] 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 89ebfa764..846b54ad4 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 180/455] 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 2d523f6aa..b1f6b421a 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 181/455] 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 4d5487f82..366ad15f9 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 182/455] 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 9bf302e0d..eb04e479c 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 0fa8c02b0..b4ea58dd2 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 45ee93911..8eda58c8f 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 f45043232..25a8ecc2f 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 b3b76f805..799c5b6b3 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 10349ae5c..7ae420053 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 ae29c3968..61a785399 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 6c3d6cfc3..10a804b4f 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 e22802c10..6e455d232 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 f6870db82..92da9d029 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 62a2302c5..1c1357c44 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 a6a07d149..662e9afbd 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 25594da19..f05d43c3e 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 a98b3c145..61d401696 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 a24536d49..0600be900 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 0c334c1ca..f20e1e87e 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 62e255ea4..df9a415ec 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 29cd92b9a..3ea8b3e63 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 5aa3ec4eb..f12b8d21e 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 3b421972e..94f2d69f5 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 656f0a212..ee1775dc2 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 cd72dec76..43e9d9f1a 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 b6b1a7200..921d1eeb9 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 93855a8b5..a14fbb126 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 7d7f2b78b..573bc5f61 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 808a12975..74630efa5 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 c86f60787..ddf3cc4a6 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 2cbf57496..7efac2df2 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 971f2919e..50a0690cc 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 501733f8a..02ae8a9f7 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 011c935a9..62e6dc624 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 a90c0e970..4dea037aa 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 1fed980f8..e18c5bbd4 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 1de3c450d..8443e0ec9 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 322b6f23b..910f8f1ef 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 503b65893..7b4e14818 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 e57c4259c..acdaaccd1 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 883b1d8f9..1b87bac89 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 aec40b278..fc18533fa 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 ff7a38f33..680df0c24 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 31de89aaf..536b578c0 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 033de1bc6..cf15be6e2 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 6ba696fce..ffc7b740b 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 0620d2b43..a1b3a5ca7 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 451a02112..dfb922782 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 371948848..b380526ed 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 5a9d9fba1..ed69527e3 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 104a17dc6..dc6368ed9 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 5921f4ff5..54a375bab 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 109dada77..941fe2b8e 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 55ce5e6c1..9e154e36d 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 9e7be2fbe..7e68ca845 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 760044127..1a57e76af 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 333d59fbd..204d77929 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 69a4dca16..c7cfcb5cf 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 cd030bad3..f96f2338c 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 efcb187a1..f7b018244 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 8f376926d..02c5c978b 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 8e57cffa8..41a6ce764 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 54e760679..6374ea62b 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 ab6f3820d..bda3cb83b 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 41341d1e3..198782062 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 a83c73132..54030fda1 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 41686c5ef..79a39a334 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 3d5cb66d2..da0baf501 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 61aa99fc9..104e850b3 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 3b084e3d0..6d79268c2 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 183/455] 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 366ad15f9..629cf759d 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 184/455] 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 629cf759d..acb59cc31 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 185/455] 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 acb59cc31..32da887df 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 186/455] 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 f4329a5ed..e314831f4 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 187/455] 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 7cdefc65c..5c8f91310 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 822526a38..ea40e4074 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 188/455] 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 e314831f4..4c84a7fcc 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 822526a38..344a7b549 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 189/455] 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 000000000..5c642ae35 --- /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 190/455] 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 85d5e7a29..3e42b88b1 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 191/455] 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 3e42b88b1..bb29b34d3 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 192/455] 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 b1f6b421a..1da1056ab 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 193/455] 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 9a5fe45f1..cea312633 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 c1035e8d5..b163a9a49 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 89ebfa764..6ca9e1424 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 2dd66aa90..c6d48cd33 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 194/455] 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 ccb8b569c..7ebb76fb3 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 195/455] 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 cea312633..130b59cba 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 b163a9a49..f1f859c15 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 6ca9e1424..96c6f3308 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 c6d48cd33..e442ef00e 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 196/455] 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 000000000..4ea77b8ac --- /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 197/455] 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 60880d579..46e48b660 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 198/455] 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 7e750a3e0..e874614f3 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 199/455] 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 595de92a2..d306256a2 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 200/455] 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 94049ff74..2f5860f8f 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 201/455] 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 700c498f6..a20c92335 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 202/455] 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 a378d2c2e..b33b1e6e8 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 203/455] 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 000000000..db2b33b0d --- /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 204/455] 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 5c642ae35..4ba8d57b7 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 205/455] 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 bb29b34d3..a94c4d97a 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 206/455] 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 9a5fe45f1..5e074e387 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 f12d66c24..23d1cb10c 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 55915d760..7ea10988c 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 378e88656..f3f051636 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 c1035e8d5..0ff613fc7 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 20cae2a09..2548b8bb8 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 2dd66aa90..fa28b5425 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 207/455] 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 a20c92335..4b2e444c9 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 208/455] 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 000000000..99d86140f --- /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 000000000..b18708ffa --- /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 209/455] 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 7ea10988c..813a8d194 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 210/455] 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 813a8d194..af4c01f1d 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 b18708ffa..be6a8929d 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 211/455] 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 5e074e387..17a83b466 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 2548b8bb8..d9a8953d2 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 212/455] 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 99d86140f..0bc2bf04a 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 213/455] 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 be6a8929d..f3d3c4b0d 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 214/455] 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 af4c01f1d..65006fa72 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 215/455] 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 f3d3c4b0d..942f4f182 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 216/455] 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 23d1cb10c..39e7f564d 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 217/455] 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 65006fa72..14cc10194 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 0bc2bf04a..36df452da 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 942f4f182..8daeb5363 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 218/455] 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 39e7f564d..0a0e51f1c 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 219/455] 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 14cc10194..a511637cc 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 d9a8953d2..77f1eb289 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 220/455] 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 85d5e7a29..30a92bd60 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 221/455] 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 e23efb486..4b2843c20 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 222/455] 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 1da1056ab..a11f6cef6 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 f1fc11600..86f69bab9 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 223/455] 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 55706a703..b9308dee0 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 224/455] 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 4b2843c20..c882e0ef6 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 225/455] 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 575ee1a65..1f247e59a 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 226/455] 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 4b2e444c9..52e7c9170 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 227/455] 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 b33b1e6e8..e90783ae0 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 1451eecaf077a62b76ea16f19f69cb613c8b3eba Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Wed, 31 Dec 2014 19:23:09 +0100 Subject: [PATCH 228/455] Re-implement ace_getAttributeOnSelection --- src/static/js/ace2_inner.js | 109 +++++++++++++++--------------------- 1 file changed, 44 insertions(+), 65 deletions(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index f1fc11600..d1b3131e9 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -2311,93 +2311,72 @@ function Ace2Inner(){ } editorInfo.ace_setAttributeOnSelection = setAttributeOnSelection; + 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; + if (!(rep.selStart && rep.selEnd)) return + var withIt = Changeset.makeAttribsString('+', [ [attributeName, 'true'] ], rep.apool); var withItRegex = new RegExp(withIt.replace(/\*/g, '\\*') + "(\\*|$)"); - function hasIt(attribs) { return withItRegex.test(attribs); } - var selStartLine = rep.selStart[0]; - var selEndLine = rep.selEnd[0]; - for (var n = selStartLine; n <= selEndLine; n++) - { - var opIter = Changeset.opIterator(rep.alines[n]); - var indexIntoLine = 0; - var selectionStartInLine = 0; - var selectionEndInLine = rep.lines.atIndex(n).text.length; // exclude newline - if(rep.lines.atIndex(n).text.length == 0){ - return false; // If the line length is 0 we basically treat it as having no formatting + return rangeHasAttrib(rep.selStart, rep.selEnd) + + function rangeHasAttrib(selStart, selEnd) { + // if range is collapsed -> no attribs in range + if(selStart[1] == selEnd[1] && selStart[0] == selEnd[0]) return false + + if(selStart[0] != selEnd[0]) { // -> More than one line selected + var hasAttrib = true + + // from selStart to the end of the first line + hasAttrib = hasAttrib && rangeHasAttrib(selStart, [selStart[0], rep.lines.atIndex(selStart[0]).text.length]) + + // for all lines in between + for(var n=selStart[0]+1; n < selEnd[0]; n++) { + hasAttrib = hasAttrib && rangeHasAttrib([n, 0], [n, rep.lines.atIndex(n).text.length]) + } + + // for the last, potentially partial, line + hasAttrib = hasAttrib && rangeHasAttrib([selEnd[0], 0], [selEnd[0], selEnd[1]]) + + return hasAttrib } - if(rep.selStart[1] == rep.selEnd[1] && rep.selStart[1] == rep.lines.atIndex(n).text.length){ - return false; // If we're at the end of a line we treat it as having no formatting - } - if(rep.selStart[1] == 0 && rep.selEnd[1] == 0){ - rep.selEnd[1] == 1; - } - if(rep.selEnd[1] == -1){ - rep.selEnd[1] = 1; // sometimes rep.selEnd is -1, not sure why.. When it is we should look at the first char - } - if (n == selStartLine) - { - selectionStartInLine = rep.selStart[1]; - } - if (n == selEndLine) - { - selectionEndInLine = rep.selEnd[1]; - } - while (opIter.hasNext()) - { + + // Logic tells us we now have a range on a single line + + var lineNum = selStart[0] + , start = selStart[1] + , end = selEnd[1] + , hasAttrib = true + + // Iterate over attribs on this line + + var opIter = Changeset.opIterator(rep.alines[lineNum]) + , indexIntoLine = 0 + + while (opIter.hasNext()) { var op = opIter.next(); var opStartInLine = indexIntoLine; var opEndInLine = opStartInLine + op.chars; - if (!hasIt(op.attribs)) - { + if (!hasIt(op.attribs)) { // does op overlap selection? - if (!(opEndInLine <= selectionStartInLine || opStartInLine >= selectionEndInLine)) - { - selectionAllHasIt = false; + if (!(opEndInLine <= start || opStartInLine >= end)) { + hasAttrib = false; // since it's overlapping but hasn't got the attrib -> range hasn't got it break; } } indexIntoLine = opEndInLine; } - if (!selectionAllHasIt) - { - break; - } - } - if(selectionAllHasIt){ - return true; - }else{ - return false; + + return hasAttrib } } + editorInfo.ace_getAttributeOnSelection = getAttributeOnSelection; function toggleAttributeOnSelection(attributeName) From 45e90e138c1abda729855f4ae301ba90783011a6 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 1 Jan 2015 16:57:31 +0000 Subject: [PATCH 229/455] 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 03e892d59..b92477d66 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 230/455] changelog --- CHANGELOG.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 052a819c0..762ef39e3 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 From 230302b132dbfab0f8342820144a58febabc40ee Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 1 Jan 2015 22:40:45 +0000 Subject: [PATCH 231/455] fix timeslider stars and frontend tests, needs css polish --- src/static/css/timeslider.css | 8 +++++++- tests/frontend/specs/timeslider_revisions.js | 3 +++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/static/css/timeslider.css b/src/static/css/timeslider.css index 4c84a7fcc..49f894210 100644 --- a/src/static/css/timeslider.css +++ b/src/static/css/timeslider.css @@ -154,11 +154,17 @@ stepper:active{ top: 20px; width: 25px; } +.star:before{ + font-family: fontawesome-etherpad; + content: "\e835"; + vertical-align:middle; + font-size:16px; +} #timeslider .star { cursor: pointer; height: 16px; position: absolute; - top: 40px; + top: 25px; width: 15px; } #timeslider #timer { diff --git a/tests/frontend/specs/timeslider_revisions.js b/tests/frontend/specs/timeslider_revisions.js index 76fde33af..2afd2e9d3 100644 --- a/tests/frontend/specs/timeslider_revisions.js +++ b/tests/frontend/specs/timeslider_revisions.js @@ -19,6 +19,7 @@ describe("timeslider", function(){ inner$("div").first().sendkeys('a'); }, timePerRev*i); } + chrome$('.buttonicon-savedRevision').click(); setTimeout(function() { // go to timeslider @@ -51,6 +52,8 @@ describe("timeslider", function(){ setTimeout(function() { //make sure the text has changed expect( timeslider$('#padcontent').text() ).not.to.eql( latestContents ); + var starIsVisible = timeslider$('.star').is(":visible"); + expect( starIsVisible ).to.eql( true ); done(); }, 1000); From e2ea82f8df23f7ba68b5873024ddef178748c8c9 Mon Sep 17 00:00:00 2001 From: Siebrand Mazeland Date: Fri, 2 Jan 2015 10:58:48 +0100 Subject: [PATCH 232/455] Localisation updates from https://translatewiki.net. --- src/locales/bgn.json | 75 ++++++++++++++++++++++++++++++++++++++++++++ src/locales/diq.json | 1 + 2 files changed, 76 insertions(+) create mode 100644 src/locales/bgn.json diff --git a/src/locales/bgn.json b/src/locales/bgn.json new file mode 100644 index 000000000..00efbf3f9 --- /dev/null +++ b/src/locales/bgn.json @@ -0,0 +1,75 @@ +{ + "@metadata": { + "authors": [ + "Baloch Afghanistan" + ] + }, + "index.newPad": "یاداشتی نوکین کتابچه", + "index.createOpenPad": "یا جوڑ\t کورتین/پاچ کورتین یک کتابچه ئی یاداشتی بی نام:", + "pad.toolbar.bold.title": "پررنگ (Ctrl-B)", + "pad.toolbar.italic.title": "چوّٹ (Ctrl-I)", + "pad.toolbar.underline.title": "جهلگ خط (Ctrl-U)", + "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": "نویسوکئ رنگانی پاک کورتین (Ctrl+Shift+C)", + "pad.toolbar.import_export.title": "بی تئ کورتین/دَر کورتین شه/بی رکم رکمین قالیبان", + "pad.toolbar.timeslider.title": "وختئ لَگوشوک", + "pad.toolbar.savedRevision.title": "نسخه ئی ذخیره کورتین", + "pad.toolbar.settings.title": "تنظیمات", + "pad.colorpicker.save": "ذخیره", + "pad.colorpicker.cancel": "کنسیل", + "pad.loading": "لودینگ...", + "pad.wrongPassword": "شمی پاسورد جووان نه اینت", + "pad.settings.padSettings": "یاداشتئ دفترچه ئی تنظیمات", + "pad.settings.myView": "نئ دیست", + "pad.settings.stickychat": "هبر موچین وختا بی دیستئ تاکدیمئ سرا بیئت", + "pad.settings.colorcheck": "نویسوکی رنگ ئان", + "pad.settings.linenocheck": "خط ئانی نمبر", + "pad.settings.rtlcheck": "محتوایی وانتین شه راست بی چپا؟", + "pad.settings.fontType": "قلم رکم:", + "pad.settings.fontType.normal": "ساددگ", + "pad.settings.fontType.monospaced": "Monospace", + "pad.settings.globalView": "سراسرین دیست یا نما", + "pad.settings.language": "زبان:", + "pad.importExport.exporthtml": "HTML", + "pad.importExport.exportplain": "ساده گین متن", + "pad.importExport.exportword": "Microsoft Word", + "pad.importExport.exportpdf": "PDF", + "pad.importExport.exportopen": "ODF (پاچین سندئ قالب)", + "pad.importExport.abiword.innerHTML": "شما تا توانیت که شه ساده گین متنی ئین قالب یا اچ‌تی‌ام‌ال بی تئ کنیت . په گیشتیرین کارا ئییان پیشرفته ئین بی تئ کورتینا AbiWord نصب کنیت.", + "pad.modals.connected": "وصل بوت.", + "pad.modals.userdup": "نوکین دروازه گئ پاچ کورتین", + "pad.modals.unauth": "مجاز نه اینت", + "pad.modals.deleted.explanation": "ای یاداشتی دفترچه پاک بوته.", + "pad.share.readonly": "فقط وانتین", + "pad.share.link": "لینک", + "pad.chat": "چت وهبر", + "timeslider.toolbar.exportlink.title": "دَر کورتین", + "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": "ڈ\tسمبر", + "timeslider.unnamedauthors": "{{num}} بی نامین نویسوک", + "pad.userlist.entername": "وتئ ناما نیویشته بکنیت", + "pad.userlist.unnamed": "بی نام", + "pad.userlist.guest": "مهمان", + "pad.userlist.deny": "رد کورتین", + "pad.userlist.approve": "قبول کورتین", + "pad.impexp.importbutton": "انون بی تئ کن", + "pad.impexp.importing": "بی بی تئ کورتینی حالا...", + "pad.impexp.uploadFailed": "آپلود انجام نه بوت، پدا کوشش کن", + "pad.impexp.copypaste": "کپی پیست کَنیت" +} diff --git a/src/locales/diq.json b/src/locales/diq.json index 61d401696..81a554772 100644 --- a/src/locales/diq.json +++ b/src/locales/diq.json @@ -48,6 +48,7 @@ "pad.modals.userdup": "Zewbina pençere de bi a", "pad.modals.unauth": "Selahiyetdar niyo", "pad.modals.initsocketfail": "Nêresneyêno ciyageyroği.", + "pad.modals.slowcommit.explanation": "Server cewab nêdano.", "pad.modals.deleted": "Esteriya.", "pad.modals.deleted.explanation": "Ena ped wedariye", "pad.share": "Na ped vıla ke", From 26c839063f2a006cc5ac28266c0a2156924c01c2 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 4 Jan 2015 14:47:08 +0000 Subject: [PATCH 233/455] check file system that abiword exists --- src/node/utils/Settings.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/node/utils/Settings.js b/src/node/utils/Settings.js index af7ede811..05ae3bd84 100644 --- a/src/node/utils/Settings.js +++ b/src/node/utils/Settings.js @@ -236,6 +236,19 @@ exports.reloadSettings = function reloadSettings() { process.env['DEBUG'] = 'socket.io:' + exports.loglevel; // Used by SocketIO for Debug log4js.replaceConsole(); + if(exports.abiword){ + // Check abiword actually exists + if(exports.abiword != null) + { + fs.exists(exports.abiword, function(exists) { + if (!exists) { + console.error("Abiword does not exist at this path, check your settings file"); + exports.abiword = null; + } + }); + } + } + if(!exports.sessionKey){ // If the secretKey isn't set we also create yet another unique value here exports.sessionKey = randomString(32); console.warn("You need to set a sessionKey value in settings.json, this will allow your users to reconnect to your Etherpad Instance if your instance restarts"); From 751adb24fde9a093e3bf57e2e8350b2ae67db52e Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Mon, 5 Jan 2015 18:38:34 +0100 Subject: [PATCH 234/455] Finally fix AttribManager#removeAttributeOnLine return safely if the attribute wasn't found. fixes #2394 --- src/static/js/AttributeManager.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/static/js/AttributeManager.js b/src/static/js/AttributeManager.js index a11f6cef6..974d8ad91 100644 --- a/src/static/js/AttributeManager.js +++ b/src/static/js/AttributeManager.js @@ -164,12 +164,20 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ var builder = Changeset.builder(this.rep.lines.totalWidth()); var hasMarker = this.lineHasMarker(lineNum); var attribs + var foundAttrib = false attribs = this.getAttributesOnLine(lineNum).map(function(attrib) { - if(attrib[0] === attributeName) return [attributeName, null] + if(attrib[0] === attributeName) { + foundAttrib = true + return [attributeName, null] // remove this attrib from the linemarker + } return attrib }) + if(!foundAttrib) { + return + } + 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 From 17ce67d936777eea7d85106bdf978661dde73bd8 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 6 Jan 2015 16:31:52 +0000 Subject: [PATCH 235/455] final html export hook --- src/node/handler/ExportHandler.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/node/handler/ExportHandler.js b/src/node/handler/ExportHandler.js index 0a0e51f1c..0654deb4d 100644 --- a/src/node/handler/ExportHandler.js +++ b/src/node/handler/ExportHandler.js @@ -158,8 +158,12 @@ exports.doExport = function(req, res, padId, type) //if this is a html export, we can send this from here directly if(type == "html") { - res.send(html); - callback("stop"); + // do any final changes the plugin might want to make cake + hooks.aCallFirst("exportHTMLSend", html, function(err, newHTML){ + if(newHTML.length) html = newHTML; + res.send(html); + callback("stop"); + }); } else //write the html export to a file { From ce156540bab1754dd569acd3532ff7c26ed73161 Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Wed, 7 Jan 2015 07:03:53 +0100 Subject: [PATCH 236/455] fix importexport-tests --- tests/frontend/specs/importexport.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/frontend/specs/importexport.js b/tests/frontend/specs/importexport.js index 4ba8d57b7..59607dba4 100644 --- a/tests/frontend/specs/importexport.js +++ b/tests/frontend/specs/importexport.js @@ -6,6 +6,9 @@ describe("import functionality", function(){ function getinnertext(){ var inner = helper.padInner$ + if(!inner){ + return "" + } var newtext = "" inner("div").each(function(line,el){ newtext += el.innerHTML+"\n" From f2c443809fe2123e32cd86b9752126f0186ebd7b Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 7 Jan 2015 15:18:54 +0000 Subject: [PATCH 237/455] roll ejs back as its broken at v2 --- src/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/package.json b/src/package.json index b92477d66..8ba31be8c 100644 --- a/src/package.json +++ b/src/package.json @@ -28,7 +28,7 @@ "cheerio" : ">=0.18.0", "async-stacktrace" : ">=0.0.2", "npm" : ">=2.1.x", - "ejs" : ">=1.0.0", + "ejs" : "~1.0.0", "graceful-fs" : ">=3.0.4", "slide" : ">=1.1.6", "semver" : ">=2.3.0", From 1f558dc8430b53f349d7161d773bc177354f3791 Mon Sep 17 00:00:00 2001 From: Jan-Erik Rediger Date: Thu, 8 Jan 2015 12:54:08 +0100 Subject: [PATCH 238/455] Removed useless use of cat --- bin/installDeps.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/installDeps.sh b/bin/installDeps.sh index 58eb32f2d..ba2f56a4b 100755 --- a/bin/installDeps.sh +++ b/bin/installDeps.sh @@ -81,9 +81,9 @@ DOWNLOAD_JQUERY="true" NEEDED_VERSION="1.9.1" if [ -f "src/static/js/jquery.js" ]; then if [ $(uname) = "SunOS" ]; then - VERSION=$(cat src/static/js/jquery.js | head -n 3 | ggrep -o "v[0-9]\.[0-9]\(\.[0-9]\)\?"); + VERSION=$(head -n 3 src/static/js/jquery.js | ggrep -o "v[0-9]\.[0-9]\(\.[0-9]\)\?") else - VERSION=$(cat src/static/js/jquery.js | head -n 3 | grep -o "v[0-9]\.[0-9]\(\.[0-9]\)\?"); + VERSION=$(head -n 3 src/static/js/jquery.js | grep -o "v[0-9]\.[0-9]\(\.[0-9]\)\?") fi if [ ${VERSION#v} = $NEEDED_VERSION ]; then From 82efcdb430e402626efb9fdef93a6aaba0d31ffa Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Fri, 9 Jan 2015 02:04:03 +0100 Subject: [PATCH 239/455] fix for uls without class --- src/static/js/contentcollector.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index e90783ae0..0dea2de05 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -538,7 +538,16 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class 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)); + if(tname == "ul"){ + if((type && type.match("indent")) || (node.attribs && node.attribs.class && node.attribs.class.match("indent"))){ + type = "indent" + } else { + type = "bullet" + } + } else { + type = "number" + } + type = type + String(Math.min(_MAX_LIST_LEVEL, (state.listNesting || 0) + 1)); } oldListTypeOrNull = (_enterList(state, type) || 'none'); } From 9b9a7471671c1773c4f203ed5493be6d5bdc5913 Mon Sep 17 00:00:00 2001 From: Timothy Gu Date: Sun, 11 Jan 2015 00:02:02 -0800 Subject: [PATCH 240/455] Support ejs 2.0 Fixes #2437. This is still relying on undocumented behavior, but should unbreak the app in the mean time. --- src/node/eejs/index.js | 2 +- src/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/node/eejs/index.js b/src/node/eejs/index.js index 30f5a442f..5f2baaf36 100644 --- a/src/node/eejs/index.js +++ b/src/node/eejs/index.js @@ -114,7 +114,7 @@ exports.require = function (name, args, mod) { args.e = exports; args.require = require; - var template = '<% e._init(buf); %>' + fs.readFileSync(ejspath).toString() + '<% e._exit(); %>'; + var template = '<% e._init([__output]); %>' + fs.readFileSync(ejspath).toString() + '<% e._exit(); %>'; exports.info.args.push(args); exports.info.file_stack.push({path: ejspath, inherit: []}); diff --git a/src/package.json b/src/package.json index 8ba31be8c..6c6123dac 100644 --- a/src/package.json +++ b/src/package.json @@ -28,7 +28,7 @@ "cheerio" : ">=0.18.0", "async-stacktrace" : ">=0.0.2", "npm" : ">=2.1.x", - "ejs" : "~1.0.0", + "ejs" : "^2.0.0", "graceful-fs" : ">=3.0.4", "slide" : ">=1.1.6", "semver" : ">=2.3.0", From a080b12eac8d571fcf14691a490b79c3f26e012c Mon Sep 17 00:00:00 2001 From: Stefan Date: Sun, 11 Jan 2015 22:40:56 +0100 Subject: [PATCH 241/455] Add new favicon (32x32) with associated svg file --- src/etherpad_icon.svg | 92 +++++++++++++++++++++++++++++++++++++++++ src/static/favicon.ico | Bin 1150 -> 4286 bytes 2 files changed, 92 insertions(+) create mode 100644 src/etherpad_icon.svg diff --git a/src/etherpad_icon.svg b/src/etherpad_icon.svg new file mode 100644 index 000000000..ebdcde99b --- /dev/null +++ b/src/etherpad_icon.svg @@ -0,0 +1,92 @@ + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/static/favicon.ico b/src/static/favicon.ico index df7b628996cd0a205dc5df5b1188342a80f34e90..938e95502402fd81aa79bc0e26feabfdfac29e2a 100644 GIT binary patch literal 4286 zcmdUyUr19?9LJ9{5bY(XBrHghY+}Ad5EkOp&HtPx6hM=ugSZ0d_! z(-t{Un{zs-$4MmALu61OR8kRLqK72GqCcL#hdg%DIqr7nLvvq$=bm$Zzt8X7bMD+0%ugw5}#UazNJU0qIb+`Mcyn?Lh@%${(Zq6n_nP1Y;N3)|J6e%b2 zHf4UA=UmJ~*8HDssHB#2Wr}2ODigW9EQ{}P6wx1dh$5{9RGcZ=|AKGSYv>izDMfO? zKh8RmHU2n$@nWS?CyQ^<_R^k;XNu&kd=f7|S$wN@fDUM=BDpo+Q|4dLwb8b!up(Wp zx+|MsQ&aO|2j_ev4{+@KH&?V^|Fi$t!Cd0*Kfrs>n2n8%lEGkL_k!&Q6sxwjcHU?- zzC)~479Z*q$IZ*@ENZvgec^EU6|^ulHPwXn4BjUhi+%1s_}@UB71$8+c)iX~xc=Gv z#A2~s@Vf-RCD^A>VKQv=?|{BAF80(V%oprMxqQxIc_7B~k&%&>`uh3h% zm}NPk{$;#2-VbpXp>5BfcuvmG&d#>+=Y{);7V&eTM-Y3T^kdvju;UPWxAcmAHpYT`Q-*0iFR7asU7T literal 1150 zcma)$F-pWx6h*Hnh}cXe+F7~`t7HM$ft71WKvM)#SlOlx!c=yeQn9nrg(Q_#aE9-H z3c+u}iw z_j`i}x#|5VEQK8&r1ugJ;^}P@mckAX(tC*q@xFV7rLe<;^j_jYy!*=#mckAX(tC*q zy&gL}Xnk3h&(k!W(xtSUZETy~tQ*JitZAAxzmc-8>qp!C<+pA7V9&Ho>haxfwqcw8 x2fokqe3<9?b{K{$OHmXPH!r)cyR&Cl`o6#U&wJgMy!rT&K96_VzDS!}=?C51Xs7@H From a553b44dbccf76603ae516464cd695a8f4fd7d6e Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 11 Jan 2015 23:59:18 +0000 Subject: [PATCH 242/455] refix plugins --- src/node/eejs/index.js | 2 +- src/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/node/eejs/index.js b/src/node/eejs/index.js index 5f2baaf36..30f5a442f 100644 --- a/src/node/eejs/index.js +++ b/src/node/eejs/index.js @@ -114,7 +114,7 @@ exports.require = function (name, args, mod) { args.e = exports; args.require = require; - var template = '<% e._init([__output]); %>' + fs.readFileSync(ejspath).toString() + '<% e._exit(); %>'; + var template = '<% e._init(buf); %>' + fs.readFileSync(ejspath).toString() + '<% e._exit(); %>'; exports.info.args.push(args); exports.info.file_stack.push({path: ejspath, inherit: []}); diff --git a/src/package.json b/src/package.json index 6c6123dac..8ba31be8c 100644 --- a/src/package.json +++ b/src/package.json @@ -28,7 +28,7 @@ "cheerio" : ">=0.18.0", "async-stacktrace" : ">=0.0.2", "npm" : ">=2.1.x", - "ejs" : "^2.0.0", + "ejs" : "~1.0.0", "graceful-fs" : ">=3.0.4", "slide" : ">=1.1.6", "semver" : ">=2.3.0", From 648ebc08d64894eb08f8c1d3b2dfe90c796b7ec4 Mon Sep 17 00:00:00 2001 From: nemetz Date: Fri, 16 Jan 2015 11:11:34 -0200 Subject: [PATCH 243/455] check for io.js Added a check for io.js --- bin/installDeps.sh | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/bin/installDeps.sh b/bin/installDeps.sh index ba2f56a4b..005758451 100755 --- a/bin/installDeps.sh +++ b/bin/installDeps.sh @@ -23,6 +23,7 @@ hash curl > /dev/null 2>&1 || { } #Is node installed? +#not checking io.js, default installation creates a symbolic link to node hash node > /dev/null 2>&1 || { echo "Please install node.js ( http://nodejs.org )" >&2 exit 1 @@ -45,9 +46,13 @@ fi #check node version NODE_VERSION=$(node --version) NODE_V_MINOR=$(echo $NODE_VERSION | cut -d "." -f 1-2) +#iojs version checking added +IOJS_VERSION=$(iojs --version) if [ ! $NODE_V_MINOR = "v0.8" ] && [ ! $NODE_V_MINOR = "v0.10" ] && [ ! $NODE_V_MINOR = "v0.11" ]; then - echo "You're running a wrong version of node, you're using $NODE_VERSION, we need v0.8.x, v0.10.x or v0.11.x" >&2 - exit 1 + if [ ! $IOJS_VERSION ]; then + echo "You're running a wrong version of node, or io.js is not installed. You're using $NODE_VERSION, we need v0.8.x, v0.10.x or v0.11.x" >&2 + exit 1 + fi fi #Get the name of the settings file From 6d6ddd7f9f06d6cbf71ee3e1cff0f7b501432f4d Mon Sep 17 00:00:00 2001 From: Stefan Date: Sun, 18 Jan 2015 12:15:41 +0100 Subject: [PATCH 244/455] Prevent form submit on plugin page --- src/static/js/admin/plugins.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/static/js/admin/plugins.js b/src/static/js/admin/plugins.js index 8a4c732e5..15406f5f4 100644 --- a/src/static/js/admin/plugins.js +++ b/src/static/js/admin/plugins.js @@ -131,6 +131,11 @@ $(document).ready(function () { $("#search-query").unbind('keyup').keyup(function () { search($("#search-query").val()); }); + + // Prevent form submit + $('#search-query').parent().bind('submit', function() { + return false; + }); // update & install $(".do-install, .do-update").unbind('click').click(function (e) { From 46440733996787eaa6c3555b53f0640a30e94cd7 Mon Sep 17 00:00:00 2001 From: Stefan Date: Sun, 18 Jan 2015 13:56:30 +0100 Subject: [PATCH 245/455] Prevent dublicates in search results --- src/static/js/admin/plugins.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/static/js/admin/plugins.js b/src/static/js/admin/plugins.js index 15406f5f4..a60ad446c 100644 --- a/src/static/js/admin/plugins.js +++ b/src/static/js/admin/plugins.js @@ -181,6 +181,7 @@ $(document).ready(function () { socket.on('results:search', function (data) { if(!data.results.length) search.end = true; + if(data.query.offset == 0) search.results = []; search.messages.hide('nothing-found') search.messages.hide('fetching') $("#search-query").removeAttr('disabled') From 3c6db077ed453c491a61707081928242f7ad4a5f Mon Sep 17 00:00:00 2001 From: Stefan Date: Sun, 18 Jan 2015 14:22:27 +0100 Subject: [PATCH 246/455] Change domain for plugins.json request --- src/static/js/pluginfw/installer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/static/js/pluginfw/installer.js b/src/static/js/pluginfw/installer.js index 90bd9aa27..35e42c171 100644 --- a/src/static/js/pluginfw/installer.js +++ b/src/static/js/pluginfw/installer.js @@ -61,7 +61,7 @@ exports.availablePlugins = null; var cacheTimestamp = 0; exports.getAvailablePlugins = function(maxCacheAge, cb) { - request("http://etherpad.org/plugins.json", function(er, response, plugins){ + request("http://static.etherpad.org/plugins.json", function(er, response, plugins){ if (er) return cb && cb(er); if(exports.availablePlugins && maxCacheAge && Math.round(+new Date/1000)-cacheTimestamp <= maxCacheAge) { return cb && cb(null, exports.availablePlugins) From 83fae2227940fb771a7dd24454417d668d147ff2 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 18 Jan 2015 15:32:48 +0000 Subject: [PATCH 247/455] allow list text entry up to 16 items --- src/static/js/contentcollector.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index 0dea2de05..b7b3c568b 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -23,7 +23,7 @@ * limitations under the License. */ -var _MAX_LIST_LEVEL = 8; +var _MAX_LIST_LEVEL = 16; var UNorm = require('unorm'); var Changeset = require('./Changeset'); @@ -523,7 +523,7 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class }else{ var type = null; } - var rr = cls && /(?:^| )list-([a-z]+[12345678])\b/.exec(cls); + var rr = cls && /(?:^| )list-([a-z]+[0-9]+)\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){ for (var i in node.children){ From ca6b729acea91d0997a96685e7b98286de163483 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 18 Jan 2015 19:58:38 +0000 Subject: [PATCH 248/455] fix for ep page view pageup/down --- src/static/js/ace2_inner.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 86f69bab9..5b847f9b7 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -3867,7 +3867,10 @@ function Ace2Inner(){ updateBrowserSelectionFromRep(); 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 - // top.console.log(caretOffsetTop); + + // sometimes the first selection is -1 which causes problems (Especially with ep_page_view) + // so use focusNode.offsetTop value. + if(caretOffsetTop === -1) caretOffsetTop = myselection.focusNode.offsetTop; setScrollY(caretOffsetTop); // set the scrollY offset of the viewport on the document }, 200); From c0a4051b7a9c4531b4c0843ccd676f60f339ec02 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 18 Jan 2015 20:12:02 +0000 Subject: [PATCH 249/455] use etherpads own yajsml --- src/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/package.json b/src/package.json index 8ba31be8c..177576205 100644 --- a/src/package.json +++ b/src/package.json @@ -11,7 +11,7 @@ { "name": "Marcel Klehr" } ], "dependencies" : { - "yajsml" : "1.1.6", + "etherpad-yajsml" : ">=0.0.1", "request" : ">=2.48.0", "etherpad-require-kernel" : ">=1.0.7", "resolve" : ">=1.0.0", From 689ced8443e4e8491cbd184027d10c953c0d8466 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 18 Jan 2015 20:15:17 +0000 Subject: [PATCH 250/455] and this one.. --- src/node/hooks/express/static.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node/hooks/express/static.js b/src/node/hooks/express/static.js index c6a227451..e5a2bff00 100644 --- a/src/node/hooks/express/static.js +++ b/src/node/hooks/express/static.js @@ -2,7 +2,7 @@ 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 Yajsml = require('etherpad-yajsml'); var _ = require("underscore"); exports.expressCreateServer = function (hook_name, args, cb) { From 7e69bc65ce507391353f6ca8d67ccc967e0f571f Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 18 Jan 2015 23:03:54 +0000 Subject: [PATCH 251/455] remove infinite load was causing pain --- src/static/js/admin/plugins.js | 29 ++--------------------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/src/static/js/admin/plugins.js b/src/static/js/admin/plugins.js index a60ad446c..a1ad624a4 100644 --- a/src/static/js/admin/plugins.js +++ b/src/static/js/admin/plugins.js @@ -26,12 +26,11 @@ $(document).ready(function () { $('#search-progress').show() search.messages.show('fetching') - storeScrollPosition() search.searching = true } search.searching = false; search.offset = 0; - search.limit = 25; + search.limit = 999; search.results = []; search.sortBy = 'name'; search.sortDir = /*DESC?*/true; @@ -43,7 +42,7 @@ $(document).ready(function () { $('.search-results .messages .'+msg+' *').show() }, hide: function(msg) { - //$('.search-results .messages').hide() + $('.search-results .messages').hide() $('.search-results .messages .'+msg+'').hide() $('.search-results .messages .'+msg+' *').hide() } @@ -104,28 +103,6 @@ $(document).ready(function () { }) } - // Infinite scroll - var scrollPosition - function storeScrollPosition() { - scrollPosition = $(window).scrollTop() - } - function restoreScrollPosition() { - setTimeout(function() { - $(window).scrollTop(scrollPosition) - }, 0) - } - - $(window).scroll(checkInfiniteScroll) - function checkInfiniteScroll() { - if(search.end || search.searching) return;// don't keep requesting if there are no more results - setTimeout(function() { - try{ - var top = $('.results>tr:last').offset().top - if($(window).scrollTop()+$(window).height() > top) search(search.searchTerm) - }catch(e){} - }, 1) - } - function updateHandlers() { // Search $("#search-query").unbind('keyup').keyup(function () { @@ -209,8 +186,6 @@ $(document).ready(function () { } search.messages.hide('fetching') $('#search-progress').hide() - restoreScrollPosition() - checkInfiniteScroll() search.searching = false }); From af70902befcade65769f335a63443f6dc5dc5ef8 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 18 Jan 2015 23:16:58 +0000 Subject: [PATCH 252/455] support deeper nested elements on exports --- src/node/utils/ExportHelper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node/utils/ExportHelper.js b/src/node/utils/ExportHelper.js index 3297c6d36..7a81bbbfa 100644 --- a/src/node/utils/ExportHelper.js +++ b/src/node/utils/ExportHelper.js @@ -55,7 +55,7 @@ exports._analyzeLine = function(text, aline, apool){ var listType = Changeset.opAttributeValue(opIter.next(), 'list', apool); if (listType){ lineMarker = 1; - listType = /([a-z]+)([12345678])/.exec(listType); + listType = /([a-z]+)([0-9+])/.exec(listType); if (listType){ line.listTypeName = listType[1]; line.listLevel = Number(listType[2]); From bd9ff770fe9f6efeba07a57e0a870ca7ecad1774 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 18 Jan 2015 23:21:34 +0000 Subject: [PATCH 253/455] reset all numbers on first item --- 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 c882e0ef6..3cecb9964 100644 --- a/src/node/utils/ExportHtml.js +++ b/src/node/utils/ExportHtml.js @@ -470,7 +470,7 @@ exports.getPadHTMLDocument = function (padId, revNum, noDocType, callback) 'ul.indent { list-style-type: none; }' + 'ol { list-style-type: none; padding-left:0;}' + - 'body > ol { counter-reset: first second; }' + + 'body > ol { counter-reset: first second third fourth fifth sixth seventh eighth ninth tenth eleventh twelth thirteenth fourteenth fifteenth sixteenth; }' + 'ol > li:before {' + 'content: counter(first) ". " ;'+ 'counter-increment: first;}' + From e50ea6ffea8e0f9879a061119382e844a7f23346 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 18 Jan 2015 23:24:20 +0000 Subject: [PATCH 254/455] whoops proper regexp fix for listNumber --- src/node/utils/ExportHelper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node/utils/ExportHelper.js b/src/node/utils/ExportHelper.js index 7a81bbbfa..297c2d7a6 100644 --- a/src/node/utils/ExportHelper.js +++ b/src/node/utils/ExportHelper.js @@ -55,7 +55,7 @@ exports._analyzeLine = function(text, aline, apool){ var listType = Changeset.opAttributeValue(opIter.next(), 'list', apool); if (listType){ lineMarker = 1; - listType = /([a-z]+)([0-9+])/.exec(listType); + listType = /([a-z]+)([0-9]+)/.exec(listType); if (listType){ line.listTypeName = listType[1]; line.listLevel = Number(listType[2]); From 6bc74a9adbbb9ad8187944853ce0aa3570df3cff Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 18 Jan 2015 23:33:29 +0000 Subject: [PATCH 255/455] more export logic handling --- src/node/utils/ExportHtml.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/node/utils/ExportHtml.js b/src/node/utils/ExportHtml.js index 3cecb9964..5e5aca843 100644 --- a/src/node/utils/ExportHtml.js +++ b/src/node/utils/ExportHtml.js @@ -470,7 +470,7 @@ exports.getPadHTMLDocument = function (padId, revNum, noDocType, callback) 'ul.indent { list-style-type: none; }' + 'ol { list-style-type: none; padding-left:0;}' + - 'body > ol { counter-reset: first second third fourth fifth sixth seventh eighth ninth tenth eleventh twelth thirteenth fourteenth fifteenth sixteenth; }' + + 'body > ol { counter-reset: first second third fourth fifth sixth seventh eigth ninth tenth eleventh twelth thirteenth fourteenth fifteenth sixteenth; }' + 'ol > li:before {' + 'content: counter(first) ". " ;'+ 'counter-increment: first;}' + @@ -504,15 +504,15 @@ exports.getPadHTMLDocument = function (padId, revNum, noDocType, callback) '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) ". ";'+ + 'content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(seventh) "." counter(eigth) "." 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) ". ";'+ + 'content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(seventh) "." counter(eigth) "." 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) ". ";'+ + 'content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(seventh) "." counter(eigth) "." counter(ninth) "." counter(tenth) "." counter(eleventh) ". ";'+ 'counter-increment: eleventh;}' + 'ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > li:before {' + From 0bd7914c3b409408d9a2b0f4f3d6b9ff95df0758 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 18 Jan 2015 23:58:47 +0000 Subject: [PATCH 256/455] dont paste on middle click of link --- src/static/js/ace2_inner.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 5b847f9b7..811c9a1f7 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -4858,6 +4858,14 @@ function Ace2Inner(){ $(document).on("click", handleIEOuterClick); } if (browser.msie) $(root).on("paste", handleIEPaste); + + // Don't paste on middle click of links + $(root).on("paste", function(e){ + if(e.target.a){ + e.preventDefault(); + } + }) + // CompositionEvent is not implemented below IE version 8 if ( !(browser.msie && browser.version < 9) && document.documentElement) { From 33c62329baa9a0fd41d2ba16158845bb892aafdc Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 19 Jan 2015 00:28:32 +0000 Subject: [PATCH 257/455] better regexp for line items --- src/static/js/ace2_inner.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 811c9a1f7..bd1f0053f 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -5106,7 +5106,7 @@ function Ace2Inner(){ { return null; } - type = /([a-z]+)[0-9+]/.exec(type); + type = /([a-z]+)[0-9]+/.exec(type); if(type[1] == "indent") { return null; @@ -5115,7 +5115,7 @@ function Ace2Inner(){ //2-find the first line of the list while(lineNum-1 >= 0 && (type=getLineListType(lineNum-1))) { - type = /([a-z]+)[0-9+]/.exec(type); + type = /([a-z]+)[0-9]+/.exec(type); if(type[1] == "indent") break; lineNum--; @@ -5135,7 +5135,7 @@ function Ace2Inner(){ while(listType = getLineListType(line)) { //apply new num - listType = /([a-z]+)([0-9+])/.exec(listType); + listType = /([a-z]+)([0-9]+)/.exec(listType); curLevel = Number(listType[2]); if(isNaN(curLevel) || listType[0] == "indent") { From 73d6030762c9fc37819668826b88e087d4e5ad24 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 19 Jan 2015 00:36:12 +0000 Subject: [PATCH 258/455] boop --- src/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/package.json b/src/package.json index 177576205..97a2dd6ae 100644 --- a/src/package.json +++ b/src/package.json @@ -11,7 +11,7 @@ { "name": "Marcel Klehr" } ], "dependencies" : { - "etherpad-yajsml" : ">=0.0.1", + "etherpad-yajsml" : ">=0.0.2", "request" : ">=2.48.0", "etherpad-require-kernel" : ">=1.0.7", "resolve" : ">=1.0.0", From 63c65f784ba2b9bb3bd1f088e97f4112128d1075 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 19 Jan 2015 01:45:49 +0000 Subject: [PATCH 259/455] semi working example --- src/locales/en.json | 1 + src/static/css/pad.css | 11 +++++++++++ src/static/js/chat.js | 14 ++++++++++++++ src/static/js/pad.js | 15 +++++++++++++++ src/templates/pad.html | 4 ++++ 5 files changed, 45 insertions(+) diff --git a/src/locales/en.json b/src/locales/en.json index 7f5846fd2..de309ba1b 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -32,6 +32,7 @@ "pad.settings.padSettings": "Pad Settings", "pad.settings.myView": "My View", "pad.settings.stickychat": "Chat always on screen", + "pad.settings.chatAndUsers": "Show Chat and Users", "pad.settings.colorcheck": "Authorship colors", "pad.settings.linenocheck": "Line numbers", "pad.settings.rtlcheck": "Read content from right to left?", diff --git a/src/static/css/pad.css b/src/static/css/pad.css index 4ba9f5750..1b6482065 100644 --- a/src/static/css/pad.css +++ b/src/static/css/pad.css @@ -914,6 +914,17 @@ input[type=checkbox] { border-left: 1px solid #ccc !important; width: 185px !important; } +.chatAndUsers{ + display:block !important; + right:0px !important; + border-radius:0px !important; + height:185px !important; + width:180px !important; +} +.chatAndUsersChat{ + top:300px !important; +} + @media screen and (max-width: 600px) { .toolbar ul li.separator { display: none; diff --git a/src/static/js/chat.js b/src/static/js/chat.js index 4cbbbaa78..82ca15bdc 100644 --- a/src/static/js/chat.js +++ b/src/static/js/chat.js @@ -54,6 +54,20 @@ var chat = (function() isStuck = false; } }, + chatAndUsers: function(status){ + if(status || $('#options-chatandusers').prop('checked')){ + padcookie.setPref("chatAndUsers", true); + chat.stickToScreen(true); + $('#options-stickychat').prop("disabled", "disabled"); + $('#users').addClass("chatAndUsers"); + $("#chatbox").addClass("chatAndUsersChat"); + }else{ + chat.stickToScreen(false); + $('#options-stickychat').prop("disabled", false); + $('#users').removeClass("chatAndUsers"); + $("#chatbox").removeClass("chatAndUsersChat"); + } + }, hide: function () { // decide on hide logic based on chat window being maximized or not diff --git a/src/static/js/pad.js b/src/static/js/pad.js index ff62f86cc..5b1736422 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.js @@ -120,6 +120,7 @@ var getParameters = [ { name: "userColor", checkVal: null, callback: function(val) { settings.globalUserColor = decodeURIComponent(val); } }, { name: "rtl", checkVal: "true", callback: function(val) { settings.rtlIsTrue = true } }, { name: "alwaysShowChat", checkVal: "true", callback: function(val) { chat.stickToScreen(); } }, + { name: "chatAndUsers", checkVal: "true", callback: function(val) { chat.chatAndUsers(); } }, { name: "lang", checkVal: null, callback: function(val) { window.html10n.localize([val, 'en']); } } ]; @@ -562,6 +563,10 @@ var pad = { chat.stickToScreen(true); // stick it to the screen $('#options-stickychat').prop("checked", true); // set the checkbox to on } + if(padcookie.getPref("chatAndUsers")){ // if we have a cookie for always showing chat then show it + chat.chatAndUsers(true); // stick it to the screen + $('#options-chatandusers').prop("checked", true); // set the checkbox to on + } if(padcookie.getPref("showAuthorshipColors") == false){ pad.changeViewOption('showAuthorColors', false); } @@ -791,6 +796,16 @@ var pad = { $('#options-stickychat').prop("checked", false); // set the checkbox for off } }, + determineChatAndUsersVisibility: function(){ + var chatAUVisCookie = padcookie.getPref('chatAndUsersVisible'); + if(chatAUVisCookie){ // if the cookie is set for chat always visible + chat.chatAndUsers(true); // stick it to the screen + $('#options-chatandusers').prop("checked", true); // set the checkbox to on + } + else{ + $('#options-chatandusers').prop("checked", false); // set the checkbox for off + } + }, determineAuthorshipColorsVisibility: function(){ var authColCookie = padcookie.getPref('showAuthorshipColors'); if (authColCookie){ diff --git a/src/templates/pad.html b/src/templates/pad.html index ce1ea2188..48b46c410 100644 --- a/src/templates/pad.html +++ b/src/templates/pad.html @@ -133,6 +133,10 @@

    +

    + + +

    From 13eff60dfea7b5ddcca15f9c4c4adab18c3e7dfe Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 19 Jan 2015 02:13:34 +0000 Subject: [PATCH 260/455] mheh I suck at css 0ip would do a way better job of this --- src/static/css/pad.css | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/static/css/pad.css b/src/static/css/pad.css index 1b6482065..0ecf91f05 100644 --- a/src/static/css/pad.css +++ b/src/static/css/pad.css @@ -918,11 +918,24 @@ input[type=checkbox] { display:block !important; right:0px !important; border-radius:0px !important; - height:185px !important; - width:180px !important; + height:200px !important; + width:182px !important; + margin:2px 0 0 0 !important; + border: none !important; + border-bottom: 1px solid #ccc !important; +} +.chatAndUsersChat > div > #titlecross{ + display:none; } .chatAndUsersChat{ - top:300px !important; + bottom:0px !important; + padding:0 !important; + margin:0 !important; + right:0 !important; + top: 315px !important; + width:182px !important; + border: none !important; + padding:5px !important; } @media screen and (max-width: 600px) { From cadb83ac5af0911e722812d934d3133735d46ced Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 19 Jan 2015 02:51:32 +0000 Subject: [PATCH 261/455] bumpage --- src/node/db/API.js | 11 ++++- src/node/handler/ImportHandler.js | 8 ++-- src/node/utils/ImportHtml.js | 2 +- tests/backend/specs/api/pad.js | 67 +++++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+), 7 deletions(-) diff --git a/src/node/db/API.js b/src/node/db/API.js index a9df2a12f..07127309d 100644 --- a/src/node/db/API.js +++ b/src/node/db/API.js @@ -410,7 +410,16 @@ exports.setHTML = function(padID, html, callback) if(ERR(err, callback)) return; // add a new changeset with the new html to the pad - importHtml.setPadHTML(pad, cleanText(html), callback); + importHtml.setPadHTML(pad, cleanText(html), function(e){ + if(e){ + callback(new customError("HTML is malformed","apierror")); + return; + }else{ + //update the clients on the pad + padMessageHandler.updatePadClients(pad, callback); + return; + } + }); //update the clients on the pad padMessageHandler.updatePadClients(pad, callback); diff --git a/src/node/handler/ImportHandler.js b/src/node/handler/ImportHandler.js index a511637cc..676986510 100644 --- a/src/node/handler/ImportHandler.js +++ b/src/node/handler/ImportHandler.js @@ -232,11 +232,9 @@ exports.doImport = function(req, res, padId) 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"); - } + importHtml.setPadHTML(pad, text, function(e){ + if(e) apiLogger.warn("Error importing, possibly caused by malformed HTML"); + }); } else { pad.setText(text); } diff --git a/src/node/utils/ImportHtml.js b/src/node/utils/ImportHtml.js index 59802f9bf..652e7fccd 100644 --- a/src/node/utils/ImportHtml.js +++ b/src/node/utils/ImportHtml.js @@ -40,7 +40,7 @@ function setPadHTML(pad, html, callback) cc.collectContent(doc); }catch(e){ apiLogger.warn("HTML was not properly formed", e); - return; // We don't process the HTML because it was bad.. + return callback(e); // We don't process the HTML because it was bad.. } var result = cc.finish(); diff --git a/tests/backend/specs/api/pad.js b/tests/backend/specs/api/pad.js index 52e7c9170..012ce4238 100644 --- a/tests/backend/specs/api/pad.js +++ b/tests/backend/specs/api/pad.js @@ -12,6 +12,7 @@ var apiVersion = 1; var testPadId = makeid(); var lastEdited = ""; var text = generateLongText(); +var ULhtml = '

    • one
    • 2

      • UL2
    '; describe('Connectivity', function(){ it('errors if can not connect', function(done) { @@ -71,6 +72,9 @@ describe('Permission', function(){ -> movePad(newPadID, originalPadId) -- Should provide consistant pad data -> getText(originalPadId) -- Should be "hello world" -> getLastEdited(padID) -- Should not be 0 + -> setHTML(padID) -- Should fail on invalid HTML + -> setHTML(padID) *3 -- Should fail on invalid HTML + -> getHTML(padID) -- Should return HTML close to posted HTML */ @@ -390,6 +394,69 @@ describe('getLastEdited', function(){ }); }) + +describe('setHTML', function(){ + it('Sets the HTML of a Pad attempting to pass ugly HTML', function(done) { + var html = "
    Hello HTML
    "; + api.get(endPoint('setHTML')+"&padID="+testPadId+"&html="+html) + .expect(function(res){ + if(res.body.code !== 1) throw new Error("Allowing crappy HTML to be imported") + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('setHTML', function(){ + it('Sets the HTML of a Pad with a bunch of weird unordered lists inserted', function(done) { + api.get(endPoint('setHTML')+"&padID=test&html="+ULhtml) + .expect(function(res){ + if(res.body.code !== 0) throw new Error("List HTML cant be imported") + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('getHTML', function(){ + // will fail due to https://github.com/ether/etherpad-lite/issues/1604 + // reminder to self this is how the HTML looks + //
      + //
    • one
    • + //
    • 2
    • + //
    + //
    + //
      + //
        + //
      • UL2
      • + //
      + //
    + // It will look right in the browser but the export will get it horriby wrong + + // This is what the export puts out + //
      + //
    • one
    • + //
    • 2
    • + //
    + //
    + //
      + // NOTE THIS IS WHAT'S MISSING + //
    • UL2
    • + //
    + //
    + + it('Gets the HTML of a Pad with a bunch of weird unordered lists inserted', function(done) { + api.get(endPoint('getHTML')+"&padID=test") + .expect(function(res){ + console.log(res.body.data.html); + if(res.body.data !== ULhtml) throw new Error("Imported HTML does not match served HTML") + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + + /* -> movePadForce Test From 85fffbe14ce5ca6f205434ce42b98eb4de12b6d6 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 19 Jan 2015 02:57:10 +0000 Subject: [PATCH 262/455] more handling --- src/node/utils/ImportHtml.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/node/utils/ImportHtml.js b/src/node/utils/ImportHtml.js index 652e7fccd..33fd91c65 100644 --- a/src/node/utils/ImportHtml.js +++ b/src/node/utils/ImportHtml.js @@ -91,6 +91,7 @@ function setPadHTML(pad, html, callback) apiLogger.debug('The changeset: ' + theChangeset); pad.setText(""); pad.appendRevision(theChangeset); + callback(null); } exports.setPadHTML = setPadHTML; From 4f637befeb449fc7e401b8a2a6624d288da86633 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 19 Jan 2015 02:59:17 +0000 Subject: [PATCH 263/455] more fixing --- src/node/db/API.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/node/db/API.js b/src/node/db/API.js index 07127309d..81dedcfef 100644 --- a/src/node/db/API.js +++ b/src/node/db/API.js @@ -420,10 +420,6 @@ exports.setHTML = function(padID, html, callback) return; } }); - - //update the clients on the pad - padMessageHandler.updatePadClients(pad, callback); - }); } From 7958f3b7232cd72e848b8922ac1ca7e2c0681515 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 19 Jan 2015 03:02:34 +0000 Subject: [PATCH 264/455] nearly fully working --- tests/backend/specs/api/pad.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/backend/specs/api/pad.js b/tests/backend/specs/api/pad.js index 012ce4238..05f6bdd1c 100644 --- a/tests/backend/specs/api/pad.js +++ b/tests/backend/specs/api/pad.js @@ -394,7 +394,6 @@ describe('getLastEdited', function(){ }); }) - describe('setHTML', function(){ it('Sets the HTML of a Pad attempting to pass ugly HTML', function(done) { var html = "
    Hello HTML
    "; @@ -409,7 +408,7 @@ describe('setHTML', function(){ describe('setHTML', function(){ it('Sets the HTML of a Pad with a bunch of weird unordered lists inserted', function(done) { - api.get(endPoint('setHTML')+"&padID=test&html="+ULhtml) + api.get(endPoint('setHTML')+"&padID="+testPadId+"&html="+ULhtml) .expect(function(res){ if(res.body.code !== 0) throw new Error("List HTML cant be imported") }) @@ -446,9 +445,9 @@ describe('getHTML', function(){ //
    it('Gets the HTML of a Pad with a bunch of weird unordered lists inserted', function(done) { - api.get(endPoint('getHTML')+"&padID=test") + api.get(endPoint('getHTML')+"&padID="+testPadId) .expect(function(res){ - console.log(res.body.data.html); + console.log("foo", res.body.data.html); if(res.body.data !== ULhtml) throw new Error("Imported HTML does not match served HTML") }) .expect('Content-Type', /json/) From 3463b16d1aab4277c24aef11fcb93c15f30b35da Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 19 Jan 2015 03:04:23 +0000 Subject: [PATCH 265/455] nearly there... --- tests/backend/specs/api/pad.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/tests/backend/specs/api/pad.js b/tests/backend/specs/api/pad.js index 05f6bdd1c..49800e6fc 100644 --- a/tests/backend/specs/api/pad.js +++ b/tests/backend/specs/api/pad.js @@ -433,16 +433,19 @@ describe('getHTML', function(){ // It will look right in the browser but the export will get it horriby wrong // This is what the export puts out + // //
      - //
    • one
    • - //
    • 2
    • + //
    • one
    • + //
    • 2
    • //
    //
    - //
      - // NOTE THIS IS WHAT'S MISSING - //
    • UL2
    • + //
        + //
          + //
        • UL2
        • + //
        //
      //
      + // it('Gets the HTML of a Pad with a bunch of weird unordered lists inserted', function(done) { api.get(endPoint('getHTML')+"&padID="+testPadId) From 3649118194f8441b84ada355f4fe432e34d14d21 Mon Sep 17 00:00:00 2001 From: Cetra Free Date: Mon, 19 Jan 2015 14:25:06 +1030 Subject: [PATCH 266/455] Update to timeslider to include base path for easier proxying --- src/static/js/timeslider.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/static/js/timeslider.js b/src/static/js/timeslider.js index b3c10b8a3..9cb274bef 100644 --- a/src/static/js/timeslider.js +++ b/src/static/js/timeslider.js @@ -60,10 +60,12 @@ function init() { var url = loc.protocol + "//" + loc.hostname + ":" + port + "/"; //find out in which subfolder we are var resource = exports.baseURL.substring(1) + 'socket.io'; + + console.log(exports.baseURL); //build up the socket io connection - socket = io.connect(url, {resource: resource}); - + socket = io.connect(url, {path: exports.baseURL + 'socket.io', resource: resource}); + //send the ready message once we're connected socket.on('connect', function() { From f1c9c58c2db481925e11c63fc7ac727802573530 Mon Sep 17 00:00:00 2001 From: Cetra Free Date: Mon, 19 Jan 2015 14:28:30 +1030 Subject: [PATCH 267/455] Update to timeslider to include base path for easier proxying --- src/static/js/timeslider.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/static/js/timeslider.js b/src/static/js/timeslider.js index 9cb274bef..ec237df5e 100644 --- a/src/static/js/timeslider.js +++ b/src/static/js/timeslider.js @@ -60,8 +60,6 @@ function init() { var url = loc.protocol + "//" + loc.hostname + ":" + port + "/"; //find out in which subfolder we are var resource = exports.baseURL.substring(1) + 'socket.io'; - - console.log(exports.baseURL); //build up the socket io connection socket = io.connect(url, {path: exports.baseURL + 'socket.io', resource: resource}); From 28aeeff154adf67e92735398a1e7625066b3133d Mon Sep 17 00:00:00 2001 From: Siebrand Mazeland Date: Mon, 19 Jan 2015 09:28:03 +0100 Subject: [PATCH 268/455] Localisation updates from https://translatewiki.net. --- src/locales/ar.json | 12 ++++---- src/locales/az.json | 61 ++++++++++++++++++++------------------ src/locales/be-tarask.json | 3 ++ src/locales/ca.json | 9 ++++-- src/locales/cs.json | 1 + src/locales/de.json | 3 ++ src/locales/el.json | 3 ++ src/locales/es.json | 3 ++ src/locales/fa.json | 3 ++ src/locales/fi.json | 4 ++- src/locales/fr.json | 3 ++ src/locales/gl.json | 3 ++ src/locales/hu.json | 1 + src/locales/ia.json | 3 ++ src/locales/lb.json | 1 + src/locales/mk.json | 3 ++ src/locales/ms.json | 3 ++ src/locales/nap.json | 38 +++++++++++++++++++++++- src/locales/pl.json | 4 ++- src/locales/pt-br.json | 6 +++- src/locales/pt.json | 4 ++- src/locales/sv.json | 3 ++ src/locales/zh-hans.json | 5 +++- src/locales/zh-hant.json | 4 +-- 24 files changed, 138 insertions(+), 45 deletions(-) diff --git a/src/locales/ar.json b/src/locales/ar.json index b4ea58dd2..b0d19fcc2 100644 --- a/src/locales/ar.json +++ b/src/locales/ar.json @@ -3,7 +3,8 @@ "authors": [ "Ali1", "Tux-tn", - "Alami" + "Alami", + "Meno25" ] }, "index.newPad": "باد جديد", @@ -11,14 +12,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": "حفظ المراجعة", @@ -46,6 +47,7 @@ "pad.importExport.import": "تحميل أي ملف نصي أو وثيقة", "pad.importExport.importSuccessful": "ناجح!", "pad.importExport.export": "تصدير الباد الحالي بصفة:", + "pad.importExport.exportetherpad": "إيثرباد", "pad.importExport.exporthtml": "إتش تي إم إل", "pad.importExport.exportplain": "نص عادي", "pad.importExport.exportword": "مايكروسوفت وورد", diff --git a/src/locales/az.json b/src/locales/az.json index 25a8ecc2f..99d3216aa 100644 --- a/src/locales/az.json +++ b/src/locales/az.json @@ -3,35 +3,36 @@ "authors": [ "AZISS", "Khan27", - "Mushviq Abdulla" + "Mushviq Abdulla", + "Wertuose" ] }, - "index.newPad": "Yeni Pad", - "index.createOpenPad": "və ya Pad-ı adı ilə yarat/aç:", + "index.newPad": "Yeni lövhə", + "index.createOpenPad": "və ya lövhəni bu adla yarat/aç:", "pad.toolbar.bold.title": "Qalın (Ctrl-B)", "pad.toolbar.italic.title": "Kursiv (Ctrl-I)", "pad.toolbar.underline.title": "Altından xətt çəkmə (Ctrl-U)", - "pad.toolbar.strikethrough.title": "Pozulma", - "pad.toolbar.ol.title": "Qaydaya salınmış siyahı", - "pad.toolbar.ul.title": "Qaydaya salınmamış siyahı", - "pad.toolbar.indent.title": "Abzas", - "pad.toolbar.unindent.title": "Çıxıntı", - "pad.toolbar.undo.title": "Geri Al (Ctrl-Z)", - "pad.toolbar.redo.title": "Qaytarmaq (Ctrl-Y)", + "pad.toolbar.strikethrough.title": "Üstdən xətləmək (Ctrl+5)", + "pad.toolbar.ol.title": "Sıralanmış siyahı (Ctrl+Shift+N)", + "pad.toolbar.ul.title": "Sırasız siyahı (Ctrl+Shift+L)", + "pad.toolbar.indent.title": "Abzas (TAB)", + "pad.toolbar.unindent.title": "Çıxıntı (Shift+TAB)", + "pad.toolbar.undo.title": "Geri qaytar (Ctrl+Z)", + "pad.toolbar.redo.title": "Qaytar (Ctrl+Y)", "pad.toolbar.clearAuthorship.title": "Müəlliflik Rənglərini Təmizlə", "pad.toolbar.import_export.title": "Müxtəlif fayl formatların(a/dan) idxal/ixrac", "pad.toolbar.timeslider.title": "Vaxt cədvəli", "pad.toolbar.savedRevision.title": "Saxlanılan Düzəlişlər", "pad.toolbar.settings.title": "Tənzimləmələr", - "pad.toolbar.embed.title": "Bu pad-ı yayımla", - "pad.toolbar.showusers.title": "Pad-da istifadəçiləri göstər", + "pad.toolbar.embed.title": "Bu lövhəni paylaş və qur", + "pad.toolbar.showusers.title": "Lövhədəki istifadəçiləri göstər", "pad.colorpicker.save": "Saxla", "pad.colorpicker.cancel": "İmtina", "pad.loading": "Yüklənir...", - "pad.passwordRequired": "Bu pad-a daxil olmaq üçün parol lazımdır", - "pad.permissionDenied": "Bu pad-a daxil olmaq üçün icazəniz yoxdur", + "pad.passwordRequired": "Bu lövhəyə daxil olmaq üçün parol lazımdır", + "pad.permissionDenied": "Bu lövhəyə daxil olmaq üçün icazəniz yoxdur", "pad.wrongPassword": "Sizin parolunuz səhvdir", - "pad.settings.padSettings": "Pad Tənzimləmələri", + "pad.settings.padSettings": "Lövhə nizamlamaları", "pad.settings.myView": "Mənim Görüntüm", "pad.settings.stickychat": "Söhbət həmişə ekranda", "pad.settings.colorcheck": "Müəlliflik rəngləri", @@ -45,18 +46,19 @@ "pad.importExport.import_export": "İdxal/İxrac", "pad.importExport.import": "Hər hansı bir mətn faylı və ya sənəd yüklə", "pad.importExport.importSuccessful": "Uğurlu!", - "pad.importExport.export": "Hazırki pad-ı ixrac etmək kimi:", + "pad.importExport.export": "Hazırkı lövhəni bu şəkildə ixrac et:", + "pad.importExport.exportetherpad": "Etherpad", "pad.importExport.exporthtml": "HTML", "pad.importExport.exportplain": "Adi mətn", "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", - "pad.importExport.exportopen": "ODF (Açıq Sənəd Formatı)", + "pad.importExport.exportopen": "ODF (açıq sənəd formatı)", "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..", + "pad.modals.reconnecting": "Sizin lövhə yenidən qoşulur..", "pad.modals.forcereconnect": "Məcbur təkrarən bağlan", "pad.modals.userdup": "Başqa pəncərədə artıq açıqdır", - "pad.modals.userdup.explanation": "Sənəd, ola bilsin ki, bu kompyuterdə, brauzerin bir neçə pəncərəsində açılmışdır.", + "pad.modals.userdup.explanation": "Bu lövhə, ola bilsin ki, bu kompüterdəki brauzerin bir neçə pəncərəsində açılmışdır.", "pad.modals.userdup.advice": "Bu pəncərədən istifadəylə yenidən qoşulun.", "pad.modals.unauth": "İcazəli deyil", "pad.modals.unauth.explanation": "Bu səhifəyə baxdığınız vaxt sizin icazəniz dəyişilib. Bərpa etmək üşün yenidən cəhd edin.", @@ -69,22 +71,22 @@ "pad.modals.slowcommit.cause": "Bu şəbəkə bağlantısında problemlər yarana bilər.", "pad.modals.badChangeset.explanation": "Etdiyiniz bir redaktə sinxronizasiya serveri tərəfindən qeyri-leqal/qanundan kənar olaraq təsbit edildi.", "pad.modals.badChangeset.cause": "Bu, yanlış server tərtibatı ya da başqa bir gözlənilməyən davranışlar nəticəsində ola bilər. Bu sizə bir xəta imiş kimi görünürsə lütfən servis nəzarətçisi ilə əlaqə yaradın. Redaktəyə davam etmək üçün yenidən qoşulmanı yoxlayın.", - "pad.modals.corruptPad.explanation": "Əldə etməyə çalışdığınız sənəd zədəlidir.", + "pad.modals.corruptPad.explanation": "Daxil olmağa çalışdığınız lövhə zədəlidir.", "pad.modals.corruptPad.cause": "Bu, yanlış server tərtibatı ya da başqa bir gözlənilməyən davranışlardan əmələ gələ bilər. Lütfən servis nəzarətçisi ilə əlaqə yaradın.", "pad.modals.deleted": "Silindi.", - "pad.modals.deleted.explanation": "Bu pad silindi.", + "pad.modals.deleted.explanation": "Bu lövhə silindi.", "pad.modals.disconnected": "Əlaqə kəsilib.", "pad.modals.disconnected.explanation": "Serverə qoşulma itirilib", "pad.modals.disconnected.cause": "Server istifadə olunmur. Əgər problem təkrarlanacaqsa, bizə bildirin.", - "pad.share": "Bu pad-ı yayımla", + "pad.share": "Bu lövhəni paylaş", "pad.share.readonly": "Yalnız oxuyun", "pad.share.link": "Keçid", "pad.share.emebdcode": "URL-ni yayımla", "pad.chat": "Söhbət", - "pad.chat.title": "Bu pad üçün chat açın.", + "pad.chat.title": "Bu lövhə üçün çat açın.", "pad.chat.loadmessages": "Daha çox mesaj yüklə", "timeslider.pageTitle": "{{appTitle}} Vaxt cədvəli", - "timeslider.toolbar.returnbutton": "Pad-a qayıt", + "timeslider.toolbar.returnbutton": "Lövhəyə qayıt", "timeslider.toolbar.authors": "Müəlliflər:", "timeslider.toolbar.authorsList": "Müəllif yoxdur", "timeslider.toolbar.exportlink.title": "İxrac", @@ -104,18 +106,19 @@ "timeslider.month.october": "Oktyabr", "timeslider.month.november": "Noyabr", "timeslider.month.december": "Dekabr", - "timeslider.unnamedauthors": "{{num}} adsız müəlliflər", + "timeslider.unnamedauthors": "{{num}} adsız {[plural(num) one: müəllif, other: müəllif]}", "pad.savedrevs.marked": "Bu versiya indi yaddaşa saxlanmış kimi nişanlandı", - "pad.userlist.entername": "Adınızı daxil et", + "pad.userlist.entername": "Adınızı daxil edin", "pad.userlist.unnamed": "adsız", "pad.userlist.guest": "Qonaq", "pad.userlist.deny": "İnkar etmək", "pad.userlist.approve": "Təsdiqləmək", - "pad.editbar.clearcolors": "Bütün sənədlərdə müəlliflik rənglərini təmizlə?", - "pad.impexp.importbutton": "İndi idxal edin", + "pad.editbar.clearcolors": "Bütün sənədlərdə müəllif rəngləri təmizlənsin?", + "pad.impexp.importbutton": "İndi idxal et", "pad.impexp.importing": "İdxal...", - "pad.impexp.confirmimport": "Faylın idxalı cari mətni yeniləyəcək. Siz əminsinizmi ki, davam etmək istəyirsiniz?", + "pad.impexp.confirmimport": "Faylın idxalı lövhədəki cari mətni yeniləyəcək. Davam etmək istədiyinizə əminsinizmi?", "pad.impexp.convertFailed": "Biz bu fayl idxal etmək mümkün deyil idi. Xahiş olunur müxtəlif sənəddən istifadə edin və ya kopyalayıb yapışdırmaq yolundan istifadə edin", + "pad.impexp.padHasData": "Biz bu faylı idxal edə bilmədik, çünki bu lövhədə düzəlişlər edilib, lütfən yeni lövhə idxal edin", "pad.impexp.uploadFailed": "Yükləmədə səhv, xahiş olunur yenə cəhd edin", "pad.impexp.importfailed": "İdxal zamanı səhv", "pad.impexp.copypaste": "Xahiş edirik kopyalayıb yapışdırın", diff --git a/src/locales/be-tarask.json b/src/locales/be-tarask.json index 61a785399..f67d10fe5 100644 --- a/src/locales/be-tarask.json +++ b/src/locales/be-tarask.json @@ -28,6 +28,7 @@ "pad.colorpicker.save": "Захаваць", "pad.colorpicker.cancel": "Скасаваць", "pad.loading": "Загрузка...", + "pad.noCookie": "Кукі ня знойдзеныя. Калі ласка, дазвольце кукі ў вашым браўзэры!", "pad.passwordRequired": "Для доступу да гэтага дакумэнта патрэбны пароль", "pad.permissionDenied": "Вы ня маеце дазволу на доступ да гэтага дакумэнта", "pad.wrongPassword": "Вы ўвялі няслушны пароль", @@ -46,6 +47,7 @@ "pad.importExport.import": "Загрузіжайце любыя тэкставыя файлы або дакумэнты", "pad.importExport.importSuccessful": "Пасьпяхова!", "pad.importExport.export": "Экспартаваць бягучы дакумэнт як:", + "pad.importExport.exportetherpad": "Etherpad", "pad.importExport.exporthtml": "HTML", "pad.importExport.exportplain": "Просты тэкст", "pad.importExport.exportword": "Microsoft Word", @@ -116,6 +118,7 @@ "pad.impexp.importing": "Імпартаваньне…", "pad.impexp.confirmimport": "Імпарт файла перазапіша цяперашні тэкст дакумэнту. Вы ўпэўненыя, што хочаце працягваць?", "pad.impexp.convertFailed": "Не атрымалася імпартаваць гэты файл. Калі ласка, выкарыстайце іншы фармат дакумэнту або скапіюйце ўручную.", + "pad.impexp.padHasData": "Мы не змаглі імпартаваць гэты файл, бо дакумэнт ужо мае зьмены, калі ласка, імпартуйце ў новы дакумэнт", "pad.impexp.uploadFailed": "Загрузка не атрымалася, калі ласка, паспрабуйце яшчэ раз", "pad.impexp.importfailed": "Памылка імпарту", "pad.impexp.copypaste": "Калі ласка, скапіюйце і ўстаўце", diff --git a/src/locales/ca.json b/src/locales/ca.json index 92da9d029..b7edc65b0 100644 --- a/src/locales/ca.json +++ b/src/locales/ca.json @@ -13,9 +13,9 @@ "pad.toolbar.bold.title": "Negreta (Ctrl-B)", "pad.toolbar.italic.title": "Cursiva (Ctrl-I)", "pad.toolbar.underline.title": "Subratllat (Ctrl-U)", - "pad.toolbar.strikethrough.title": "Ratllat", - "pad.toolbar.ol.title": "Llista ordenada", - "pad.toolbar.ul.title": "Llista sense ordenar", + "pad.toolbar.strikethrough.title": "Ratllat (Ctrl+5)", + "pad.toolbar.ol.title": "Llista ordenada (Ctrl+Shift+N)", + "pad.toolbar.ul.title": "Llista sense ordenar (Ctrl+Shift+L)", "pad.toolbar.indent.title": "Sagnat (TAB)", "pad.toolbar.unindent.title": "Sagnat invers (Majúsc+TAB)", "pad.toolbar.undo.title": "Desfés (Ctrl-Z)", @@ -30,6 +30,7 @@ "pad.colorpicker.save": "Desa", "pad.colorpicker.cancel": "Cancel·la", "pad.loading": "S'està carregant...", + "pad.noCookie": "No s'ha trobat la galeta. Permeteu les galetes en el navegador!", "pad.passwordRequired": "Us cal una contrasenya per a accedir a aquest pad", "pad.permissionDenied": "No teniu permisos per a accedir a aquest pad", "pad.wrongPassword": "La contrasenya és incorrecta", @@ -48,6 +49,7 @@ "pad.importExport.import": "Puja qualsevol fitxer de text o document", "pad.importExport.importSuccessful": "Hi ha hagut èxit!", "pad.importExport.export": "Exporta el pad actual com a:", + "pad.importExport.exportetherpad": "Etherpad", "pad.importExport.exporthtml": "HTML", "pad.importExport.exportplain": "Text net", "pad.importExport.exportword": "Microsoft Word", @@ -118,6 +120,7 @@ "pad.impexp.importing": "Important...", "pad.impexp.confirmimport": "En importar un fitxer se sobreescriurà el text actual del pad. Esteu segur que voleu continuar?", "pad.impexp.convertFailed": "No és possible d'importar aquest fitxer. Si us plau, podeu provar d'utilitzar un format diferent o copiar i enganxar manualment.", + "pad.impexp.padHasData": "No vam poder importar el fitxer perquè el pad ja tenia canvis. Importeu-lo a un nou pad", "pad.impexp.uploadFailed": "Ha fallat la càrrega. Torneu-ho a provar", "pad.impexp.importfailed": "Ha fallat la importació", "pad.impexp.copypaste": "Si us plau, copieu i enganxeu", diff --git a/src/locales/cs.json b/src/locales/cs.json index 1c1357c44..2a6b5fec1 100644 --- a/src/locales/cs.json +++ b/src/locales/cs.json @@ -48,6 +48,7 @@ "pad.importExport.import": "Nahrát libovolný textový soubor nebo dokument", "pad.importExport.importSuccessful": "Úspěšně!", "pad.importExport.export": "Exportovat stávající Pad jako:", + "pad.importExport.exportetherpad": "Etherpad", "pad.importExport.exporthtml": "HTML", "pad.importExport.exportplain": "Prostý text", "pad.importExport.exportword": "Microsoft Word", diff --git a/src/locales/de.json b/src/locales/de.json index f05d43c3e..a2bca7237 100644 --- a/src/locales/de.json +++ b/src/locales/de.json @@ -29,6 +29,7 @@ "pad.colorpicker.save": "Speichern", "pad.colorpicker.cancel": "Abbrechen", "pad.loading": "Laden …", + "pad.noCookie": "Das Cookie konnte nicht gefunden werden. Bitte erlaube Cookies in deinem Browser!", "pad.passwordRequired": "Sie benötigen ein Passwort, um auf dieses Pad zuzugreifen", "pad.permissionDenied": "Sie haben keine Berechtigung, um auf dieses Pad zuzugreifen", "pad.wrongPassword": "Ihr Passwort war falsch", @@ -47,6 +48,7 @@ "pad.importExport.import": "Text-Datei oder Dokument hochladen", "pad.importExport.importSuccessful": "Erfolgreich!", "pad.importExport.export": "Aktuelles Pad exportieren als:", + "pad.importExport.exportetherpad": "Etherpad", "pad.importExport.exporthtml": "HTML", "pad.importExport.exportplain": "Textdatei", "pad.importExport.exportword": "Microsoft Word", @@ -117,6 +119,7 @@ "pad.impexp.importing": "Importiere …", "pad.impexp.confirmimport": "Das Importieren einer Datei überschreibt den aktuellen Text des Pads. Wollen Sie wirklich fortfahren?", "pad.impexp.convertFailed": "Wir können diese Datei nicht importieren. Bitte verwenden Sie ein anderes Dokumentformat oder übertragen Sie den Text manuell.", + "pad.impexp.padHasData": "Wir konnten diese Datei nicht importieren, da dieses Pad bereits Änderungen hat. Bitte importiere zu einem neuen Pad.", "pad.impexp.uploadFailed": "Der Upload ist fehlgeschlagen. Bitte versuchen Sie es erneut.", "pad.impexp.importfailed": "Import fehlgeschlagen", "pad.impexp.copypaste": "Bitte kopieren und einfügen", diff --git a/src/locales/el.json b/src/locales/el.json index f20e1e87e..740da95c0 100644 --- a/src/locales/el.json +++ b/src/locales/el.json @@ -30,6 +30,7 @@ "pad.colorpicker.save": "Αποθήκευση", "pad.colorpicker.cancel": "Άκυρο", "pad.loading": "Φόρτωση...", + "pad.noCookie": "Το cookie δεν βρέθηκε. Παρακαλώ επιτρέψτε τα cookies στον περιηγητή σας!", "pad.passwordRequired": "Χρειάζεστε κωδικό πρόσβασης για πρόσβαση σε αυτό το pad", "pad.permissionDenied": "Δεν έχετε δικαίωμα πρόσβασης σε αυτό το pad", "pad.wrongPassword": "Ο κωδικός σας ήταν λανθασμένος", @@ -48,6 +49,7 @@ "pad.importExport.import": "Αποστολή οποιουδήποτε αρχείου κειμένου ή εγγράφου", "pad.importExport.importSuccessful": "Επιτυχής!", "pad.importExport.export": "Εξαγωγή τρέχοντος pad ως:", + "pad.importExport.exportetherpad": "Etherpad", "pad.importExport.exporthtml": "HTML", "pad.importExport.exportplain": "Απλό κείμενο", "pad.importExport.exportword": "Microsoft Word", @@ -118,6 +120,7 @@ "pad.impexp.importing": "Εισάγεται...", "pad.impexp.confirmimport": "Η εισαγωγή ενός αρχείου θα αντικαταστήσει το κείμενο του pad. Είστε βέβαιοι ότι θέλετε να συνεχίσετε;", "pad.impexp.convertFailed": "Δεν καταφέραμε να εισάγουμε αυτό το αρχείο. Παρακαλώ χρησιμοποιήστε διαφορετικό τύπο αρχείου ή αντιγράψτε και επικολλήστε χειροκίνητα", + "pad.impexp.padHasData": "Δεν μπορέσαμε να εισάγουμε το αρχείο επειδή το Pad είχε ήδη αλλαγές. Παρακαλούμε εισαγάγετε το αρχείο σε νέο pad", "pad.impexp.uploadFailed": "Η αποστολή απέτυχε, παρακαλώ προσπαθήστε ξανά", "pad.impexp.importfailed": "Η εισαγωγή απέτυχε", "pad.impexp.copypaste": "Παρακαλώ αντιγράψτε και επικολλήστε", diff --git a/src/locales/es.json b/src/locales/es.json index df9a415ec..5547d3278 100644 --- a/src/locales/es.json +++ b/src/locales/es.json @@ -35,6 +35,7 @@ "pad.colorpicker.save": "Guardar", "pad.colorpicker.cancel": "Cancelar", "pad.loading": "Cargando...", + "pad.noCookie": "La cookie no se pudo encontrar. ¡Por favor, habilita las cookies en tu navegador!", "pad.passwordRequired": "Necesitas una contraseña para acceder a este pad", "pad.permissionDenied": "No tienes permiso para acceder a este pad", "pad.wrongPassword": "La contraseña era incorrecta", @@ -53,6 +54,7 @@ "pad.importExport.import": "Subir cualquier texto o documento", "pad.importExport.importSuccessful": "¡Éxito!", "pad.importExport.export": "Exporta el pad actual como:", + "pad.importExport.exportetherpad": "Etherpad", "pad.importExport.exporthtml": "HTML", "pad.importExport.exportplain": "Texto plano", "pad.importExport.exportword": "Microsoft Word", @@ -123,6 +125,7 @@ "pad.impexp.importing": "Importando...", "pad.impexp.confirmimport": "Al importar un archivo se borrará el contenido actual del pad. ¿Estás seguro de que quieres continuar?", "pad.impexp.convertFailed": "No pudimos importar este archivo. Inténtalo con un formato diferente o copia y pega manualmente.", + "pad.impexp.padHasData": "No hemos podido importar este archivo porque esta almohadilla ya ha tenido cambios, por favor, importa a una nueva almohadilla", "pad.impexp.uploadFailed": "El envío falló. Intentalo de nuevo.", "pad.impexp.importfailed": "Fallo al importar", "pad.impexp.copypaste": "Intenta copiar y pegar", diff --git a/src/locales/fa.json b/src/locales/fa.json index 94f2d69f5..53fb55b21 100644 --- a/src/locales/fa.json +++ b/src/locales/fa.json @@ -31,6 +31,7 @@ "pad.colorpicker.save": "ذخیره", "pad.colorpicker.cancel": "لغو", "pad.loading": "در حال بارگذاری...", + "pad.noCookie": "کوکی یافت نشد. لطفاً اجازهٔ اجرای کوکی در مروگرتان را بدهید!", "pad.passwordRequired": "برای دسترسی به این دفترچه یادداشت نیاز به یک گذرواژه دارید", "pad.permissionDenied": "شما اجازه‌ی دسترسی به این دفترچه یادداشت را ندارید", "pad.wrongPassword": "گذرواژه‌ی شما درست نیست", @@ -49,6 +50,7 @@ "pad.importExport.import": "بارگذاری پرونده‌ی متنی یا سند", "pad.importExport.importSuccessful": "موفقیت آمیز بود!", "pad.importExport.export": "برون‌ریزی این دفترچه یادداشت با قالب:", + "pad.importExport.exportetherpad": "اترپد", "pad.importExport.exporthtml": "HTML", "pad.importExport.exportplain": "متن ساده", "pad.importExport.exportword": "Microsoft Word", @@ -119,6 +121,7 @@ "pad.impexp.importing": "در حال درون‌ریزی...", "pad.impexp.confirmimport": "با درون‌ریزی یک پرونده نوشتهٔ کنونی دفترچه پاک می‌شود. آیا می‌خواهید ادامه دهید؟", "pad.impexp.convertFailed": "ما نمی‌توانیم این پرونده را درون‌ریزی کنیم. خواهشمندیم قالب دیگری برای سندتان انتخاب کرده یا بصورت دستی آنرا کپی کنید", + "pad.impexp.padHasData": "امکان درون‌ریز این پرونده نیست زیرا این پد تغییر کرده‌است. لطفاً در پد جدید درون‌ریزی کنید.", "pad.impexp.uploadFailed": "آپلود انجام نشد، دوباره تلاش کنید", "pad.impexp.importfailed": "درون‌ریزی انجام نشد", "pad.impexp.copypaste": "کپی پیست کنید", diff --git a/src/locales/fi.json b/src/locales/fi.json index ee1775dc2..25e4d084d 100644 --- a/src/locales/fi.json +++ b/src/locales/fi.json @@ -10,7 +10,8 @@ "Tomi Toivio", "Veikk0.ma", "VezonThunder", - "Macofe" + "Macofe", + "MrTapsa" ] }, "index.newPad": "Uusi muistio", @@ -35,6 +36,7 @@ "pad.colorpicker.save": "Tallenna", "pad.colorpicker.cancel": "Peru", "pad.loading": "Ladataan…", + "pad.noCookie": "Evästettä ei löytynyt. Ole hyvä, ja salli evästeet selaimessasi!", "pad.passwordRequired": "Tämä muistio on suojattu salasanalla.", "pad.permissionDenied": "Käyttöoikeutesi eivät riitä tämän muistion käyttämiseen.", "pad.wrongPassword": "Väärä salasana", diff --git a/src/locales/fr.json b/src/locales/fr.json index 921d1eeb9..92fcb193a 100644 --- a/src/locales/fr.json +++ b/src/locales/fr.json @@ -43,6 +43,7 @@ "pad.colorpicker.save": "Enregistrer", "pad.colorpicker.cancel": "Annuler", "pad.loading": "Chargement…", + "pad.noCookie": "Le cookie n’a pas pu être trouvé. Veuillez autoriser les cookies dans votre navigateur !", "pad.passwordRequired": "Vous avez besoin d'un mot de passe pour accéder à ce pad", "pad.permissionDenied": "Il ne vous est pas permis d’accéder à ce pad", "pad.wrongPassword": "Votre mot de passe est incorrect", @@ -61,6 +62,7 @@ "pad.importExport.import": "Charger un texte ou un document", "pad.importExport.importSuccessful": "Réussi !", "pad.importExport.export": "Exporter le pad actuel comme :", + "pad.importExport.exportetherpad": "Etherpad", "pad.importExport.exporthtml": "HTML", "pad.importExport.exportplain": "Texte brut", "pad.importExport.exportword": "Microsoft Word", @@ -131,6 +133,7 @@ "pad.impexp.importing": "Import en cours...", "pad.impexp.confirmimport": "Importer un fichier écrasera le texte actuel du pad. Êtes-vous sûr de vouloir le faire ?", "pad.impexp.convertFailed": "Nous ne pouvons pas importer ce fichier. Veuillez utiliser un autre format de document ou faire un copier/coller manuel", + "pad.impexp.padHasData": "Nous n’avons pas pu importer ce fichier parce que ce bloc a déjà eu des modifications ; veuillez importer vers un nouveau bloc", "pad.impexp.uploadFailed": "Le téléchargement a échoué, veuillez réessayer", "pad.impexp.importfailed": "Échec de l'importation", "pad.impexp.copypaste": "Veuillez copier/coller", diff --git a/src/locales/gl.json b/src/locales/gl.json index a14fbb126..b0ca6532e 100644 --- a/src/locales/gl.json +++ b/src/locales/gl.json @@ -27,6 +27,7 @@ "pad.colorpicker.save": "Gardar", "pad.colorpicker.cancel": "Cancelar", "pad.loading": "Cargando...", + "pad.noCookie": "A cookie non se puido atopar. Por favor, habilite as cookies no seu navegador!", "pad.passwordRequired": "Cómpre un contrasinal para acceder a este documento", "pad.permissionDenied": "Non ten permiso para acceder a este documento", "pad.wrongPassword": "O contrasinal era incorrecto", @@ -45,6 +46,7 @@ "pad.importExport.import": "Cargar un ficheiro de texto ou documento", "pad.importExport.importSuccessful": "Correcto!", "pad.importExport.export": "Exportar o documento actual en formato:", + "pad.importExport.exportetherpad": "Etherpad", "pad.importExport.exporthtml": "HTML", "pad.importExport.exportplain": "Texto simple", "pad.importExport.exportword": "Microsoft Word", @@ -115,6 +117,7 @@ "pad.impexp.importing": "Importando...", "pad.impexp.confirmimport": "A importación dun ficheiro ha sobrescribir o texto actual do documento. Está seguro de querer continuar?", "pad.impexp.convertFailed": "Non somos capaces de importar o ficheiro. Utilice un formato de documento diferente ou copie e pegue manualmente", + "pad.impexp.padHasData": "Non puidemos importar este ficheiro porque este Pad xa tivo cambios, por favor, importe a un novo pad.", "pad.impexp.uploadFailed": "Houbo un erro ao cargar o ficheiro; inténteo de novo", "pad.impexp.importfailed": "Fallou a importación", "pad.impexp.copypaste": "Copie e pegue", diff --git a/src/locales/hu.json b/src/locales/hu.json index 7efac2df2..3102790d7 100644 --- a/src/locales/hu.json +++ b/src/locales/hu.json @@ -118,6 +118,7 @@ "pad.impexp.importing": "Importálás…", "pad.impexp.confirmimport": "Egy fájl importálása felülírja a jelenlegi szöveget a noteszben. Biztos hogy folytatod?", "pad.impexp.convertFailed": "Nem tudtuk importálni ezt a fájlt. Kérjük, használj másik dokumentum formátumot, vagy kézzel másold és illeszd be a tartalmat", + "pad.impexp.padHasData": "Nem tudjuk importálni ezt a fájlt, mert ez a Pad már megváltozott, kérjük, importálj egy új padra", "pad.impexp.uploadFailed": "A feltöltés sikertelen, próbáld meg újra", "pad.impexp.importfailed": "Az importálás nem sikerült", "pad.impexp.copypaste": "Kérjük másold be", diff --git a/src/locales/ia.json b/src/locales/ia.json index 50a0690cc..e7f5cc2b2 100644 --- a/src/locales/ia.json +++ b/src/locales/ia.json @@ -26,6 +26,7 @@ "pad.colorpicker.save": "Salveguardar", "pad.colorpicker.cancel": "Cancellar", "pad.loading": "Cargamento…", + "pad.noCookie": "Le cookie non pote esser trovate. Per favor permitte le cookies in tu navigator!", "pad.passwordRequired": "Un contrasigno es necessari pro acceder a iste pad", "pad.permissionDenied": "Tu non ha le permission de acceder a iste pad", "pad.wrongPassword": "Le contrasigno es incorrecte", @@ -44,6 +45,7 @@ "pad.importExport.import": "Incargar qualcunque file de texto o documento", "pad.importExport.importSuccessful": "Succedite!", "pad.importExport.export": "Exportar le pad actual como:", + "pad.importExport.exportetherpad": "Etherpad", "pad.importExport.exporthtml": "HTML", "pad.importExport.exportplain": "Texto simple", "pad.importExport.exportword": "Microsoft Word", @@ -114,6 +116,7 @@ "pad.impexp.importing": "Importation in curso…", "pad.impexp.confirmimport": "Le importation de un file superscribera le texto actual del pad. Es tu secur de voler continuar?", "pad.impexp.convertFailed": "Nos non ha potite importar iste file. Per favor usa un altere formato de documento o copia e colla le texto manualmente.", + "pad.impexp.padHasData": "Nos non ha potite importar iste file perque iste Pad ha jam habite cambiamentos. Per favor importa lo a un nove pad.", "pad.impexp.uploadFailed": "Le incargamento ha fallite. Per favor reproba.", "pad.impexp.importfailed": "Importation fallite", "pad.impexp.copypaste": "Per favor copia e colla", diff --git a/src/locales/lb.json b/src/locales/lb.json index 841add75f..68bdb418c 100644 --- a/src/locales/lb.json +++ b/src/locales/lb.json @@ -15,6 +15,7 @@ "pad.colorpicker.save": "Späicheren", "pad.colorpicker.cancel": "Ofbriechen", "pad.loading": "Lueden...", + "pad.noCookie": "Cookie gouf net fonnt. Erlaabt w.e.g. Cookien an Ärem Browser!", "pad.wrongPassword": "Äert Passwuert ass falsch", "pad.settings.fontType.normal": "Normal", "pad.settings.language": "Sprooch:", diff --git a/src/locales/mk.json b/src/locales/mk.json index fc18533fa..9fc6b817c 100644 --- a/src/locales/mk.json +++ b/src/locales/mk.json @@ -27,6 +27,7 @@ "pad.colorpicker.save": "Зачувај", "pad.colorpicker.cancel": "Откажи", "pad.loading": "Вчитувам...", + "pad.noCookie": "Не можев да го најдам колачето. Овозможете колачиња во вашиот прелистувач!", "pad.passwordRequired": "Потребна е лозинка за пристап", "pad.permissionDenied": "За овде не е потребна дозвола за пристап", "pad.wrongPassword": "Погрешна лозинка", @@ -45,6 +46,7 @@ "pad.importExport.import": "Подигање на било каква текстуална податотека или документ", "pad.importExport.importSuccessful": "Успешно!", "pad.importExport.export": "Извези ја тековната тетратка како", + "pad.importExport.exportetherpad": "Etherpad", "pad.importExport.exporthtml": "HTML", "pad.importExport.exportplain": "Прост текст", "pad.importExport.exportword": "Microsoft Word", @@ -115,6 +117,7 @@ "pad.impexp.importing": "Увезувам...", "pad.impexp.confirmimport": "Увезувајќи ја податотеката ќе го замените целиот досегашен текст во тетратката. Дали сте сигурни дека сакате да продолжите?", "pad.impexp.convertFailed": "Не можев да ја увезам податотеката. Послужете се со поинаков формат или прекопирајте го текстот рачно.", + "pad.impexp.padHasData": "Не можевме да ја увеземе оваа податотека бидејќи оваа тетратка веќе има промени. Увезете ја во нова тетратка.", "pad.impexp.uploadFailed": "Подигањето не успеа. Обидете се повторно.", "pad.impexp.importfailed": "Увозот не успеа", "pad.impexp.copypaste": "Прекопирајте", diff --git a/src/locales/ms.json b/src/locales/ms.json index cf15be6e2..d099de060 100644 --- a/src/locales/ms.json +++ b/src/locales/ms.json @@ -26,6 +26,7 @@ "pad.colorpicker.save": "Simpan", "pad.colorpicker.cancel": "Batalkan", "pad.loading": "Sedang dimuatkan...", + "pad.noCookie": "Cookie tidak dapat dijumpai. Tolong benarkan cookie dalam pelayar anda!", "pad.passwordRequired": "Anda memerlukan kata laluan untuk mengakses pad ini", "pad.permissionDenied": "Anda tiada kebenaran untuk mengakses pad ini", "pad.wrongPassword": "Kata laluan anda salah", @@ -44,6 +45,7 @@ "pad.importExport.import": "Muat naik sebarang fail teks atau dokumen", "pad.importExport.importSuccessful": "Berjaya!", "pad.importExport.export": "Eksport pad semasa sebagai:", + "pad.importExport.exportetherpad": "Etherpad", "pad.importExport.exporthtml": "HTML", "pad.importExport.exportplain": "Teks biasa", "pad.importExport.exportword": "Microsoft Word", @@ -114,6 +116,7 @@ "pad.impexp.importing": "Sedang mengimport...", "pad.impexp.confirmimport": "Mengimport fail akan menulis ganti teks semasa pada pad ini. Adakah anda benar-benar ingin teruskan?", "pad.impexp.convertFailed": "Fail tidak dapat diimport. Sila gunakan format dokumen yang lain atau salin tampal secara manual", + "pad.impexp.padHasData": "Kami tidak dapat mengimport fail ini kerana Pad ini sudah mengalami perubahan. Sila import ke pad yang baru", "pad.impexp.uploadFailed": "Muat naik gagal, sila cuba lagi", "pad.impexp.importfailed": "Import gagal", "pad.impexp.copypaste": "Sila salin tampal", diff --git a/src/locales/nap.json b/src/locales/nap.json index ffc7b740b..6cd1651b0 100644 --- a/src/locales/nap.json +++ b/src/locales/nap.json @@ -1,7 +1,8 @@ { "@metadata": { "authors": [ - "Chelin" + "Chelin", + "C.R." ] }, "index.newPad": "Novo Pad", @@ -21,23 +22,58 @@ "pad.toolbar.timeslider.title": "Presentazzione cronologgia", "pad.toolbar.savedRevision.title": "Sarva revisione", "pad.toolbar.settings.title": "Mpustaziune", + "pad.toolbar.embed.title": "Sparte e nzerta stu Pad", + "pad.toolbar.showusers.title": "Mmusta ll'utente ncopp'a stu Pad", "pad.colorpicker.save": "Sarva", "pad.colorpicker.cancel": "Canciella", "pad.loading": "Carecamiento 'n curso…", + "pad.noCookie": "Cookie nun truvata. Pe' piacere premmettete 'e cookies dint' 'o navigatóre vuosto!", "pad.passwordRequired": "Pe' accede a chisto Pad è necessaria 'na password", "pad.permissionDenied": "Nun se dispunne d\"e permisse necessare pe' accede a chisto Pad", "pad.wrongPassword": "'A password è sbagliata", "pad.settings.padSettings": "Mpostazzione d\"o pad", + "pad.settings.myView": "Mia Veruta", + "pad.settings.stickychat": "Chat sempe ncopp' 'o schermo", + "pad.settings.colorcheck": "Auturevolezza pe' culure", + "pad.settings.linenocheck": "Nummere 'e riga", + "pad.settings.rtlcheck": "Lieggere 'e cuntenute 'a destra a smerza?", + "pad.settings.fontType": "Tipo 'e funte:", "pad.settings.fontType.normal": "Nurmale", + "pad.settings.fontType.monospaced": "Monospace", + "pad.settings.globalView": "Visualizzazione globbale", + "pad.settings.language": "Llengua:", + "pad.importExport.import_export": "Mpurtaziune/sportaziune", + "pad.importExport.import": "Carreca coccherunto testo o documento", + "pad.importExport.importSuccessful": "Ngarrata!", + "pad.importExport.export": "Sportà stu Pad comme:", + "pad.importExport.exportetherpad": "Etherpad", "pad.importExport.exporthtml": "HTML", + "pad.importExport.exportplain": "Testo nurmale", + "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", + "pad.importExport.abiword.innerHTML": "Putite surtanto mpurtà testo chiano o furmatte HTML. Pe n'avé sisteme cchiù annanze 'e mpurtazione pe' piacere installate Abiword.", "pad.modals.connected": "Cunnesso.", "pad.modals.reconnecting": "Ricunnessione ô pad 'n curso...", "pad.modals.forcereconnect": "Forza 'a ricunnessione", "pad.modals.userdup": "Aprito 'n n'ata fenesta", + "pad.modals.userdup.explanation": "Stu Pad pare fosse araputo dint'a cchiù 'e na fenesta 'e navigatore dint'a stu computer.", + "pad.modals.userdup.advice": "Riconnettateve pe' putè ausà mmece sta fenesta.", "pad.modals.unauth": "Nun autorizzato", + "pad.modals.unauth.explanation": "'E premmesse vuoste so' cagnate pe' tramente ca se vereva sta paggena. Tentate 'e ve riconnettà.", + "pad.modals.looping.explanation": "Ce stanno probbleme 'e comunicazione c' 'o server 'e sincronizzaziona.", + "pad.modals.looping.cause": "Può darse ca ve site cullegato pe' mmiez' 'e nu firewall incompatibbele o proxy.", + "pad.modals.initsocketfail": "Nun se può arrevà 'o server.", + "pad.modals.initsocketfail.explanation": "Nun se può cunnettà 'o server e sincronizzaziona.", + "pad.modals.initsocketfail.cause": "Stu fatto è succiesso, probabbilmente pe' bbìa 'e nu probblema c' 'o navigatóre 'o ll'internet.", + "pad.modals.slowcommit.explanation": "'O server nun risponne.", + "pad.modals.slowcommit.cause": "Stu fatto può darse ca è causato pe' bbìa 'e prubbleme 'e connettività 'e rezza.", + "pad.modals.badChangeset.explanation": "Nu cagnamento ca stavate facenno è stato classeficato comme illegale p' 'o server 'e sincronizzaziona.", + "pad.modals.badChangeset.cause": "Chistu fatto può darse ca è causato pe' bbìa 'e na mpustazione errata d' 'o server o cocch'atu comportamento nun preveduto. Pe' piacere cuntattate l'ammenistratore d' 'o servizio, si se pienza ca chist'è n'errore. Tentate a ve riconnettà pe' cuntinuà 'a edità.", + "pad.modals.corruptPad.explanation": "'O pad addò vulevate trasì è scassato.", "pad.modals.deleted": "Canciellato.", + "pad.share.link": "Jonta", + "pad.chat": "Chiàcchiera", "timeslider.pageTitle": "Cronologgia {{appTitle}}", "timeslider.toolbar.returnbutton": "Ritorna ô Pad", "timeslider.toolbar.authors": "Auture:", diff --git a/src/locales/pl.json b/src/locales/pl.json index 7e68ca845..bcd7a0ef2 100644 --- a/src/locales/pl.json +++ b/src/locales/pl.json @@ -5,7 +5,8 @@ "Ty221", "WTM", "Woytecr", - "Macofe" + "Macofe", + "Pan Cube" ] }, "index.newPad": "Nowy dokument", @@ -48,6 +49,7 @@ "pad.importExport.import": "Prześlij dowolny plik tekstowy lub dokument", "pad.importExport.importSuccessful": "Sukces!", "pad.importExport.export": "Eksportuj bieżący dokument jako:", + "pad.importExport.exportetherpad": "Etherpad", "pad.importExport.exporthtml": "HTML", "pad.importExport.exportplain": "Zwykły tekst", "pad.importExport.exportword": "Microsoft Word", diff --git a/src/locales/pt-br.json b/src/locales/pt-br.json index 204d77929..e8eb79eef 100644 --- a/src/locales/pt-br.json +++ b/src/locales/pt-br.json @@ -10,7 +10,8 @@ "Rafaelff", "Dianakc", "Macofe", - "Rodrigo codignoli" + "Rodrigo codignoli", + "Webysther" ] }, "index.newPad": "Nova Nota", @@ -35,6 +36,7 @@ "pad.colorpicker.save": "Salvar", "pad.colorpicker.cancel": "Cancelar", "pad.loading": "Carregando...", + "pad.noCookie": "Cookie não foi encontrado. Por favor, habilite cookies no seu navegador!", "pad.passwordRequired": "Você precisa de uma senha para acessar esta Nota", "pad.permissionDenied": "Você não tem permissão para acessar esta Nota", "pad.wrongPassword": "Senha incorreta", @@ -53,6 +55,7 @@ "pad.importExport.import": "Enviar um arquivo texto ou documento", "pad.importExport.importSuccessful": "Completo!", "pad.importExport.export": "Exportar a presente nota como:", + "pad.importExport.exportetherpad": "Etherpad", "pad.importExport.exporthtml": "HTML", "pad.importExport.exportplain": "Texto puro", "pad.importExport.exportword": "Microsoft Word", @@ -123,6 +126,7 @@ "pad.impexp.importing": "Importando...", "pad.impexp.confirmimport": "Importar um arquivo sobrescreverá o atual texto da nota. Tem certeza de que deseja prosseguir?", "pad.impexp.convertFailed": "Não foi possível importar este arquivo. Use outro formato ou copie e cole manualmente", + "pad.impexp.padHasData": "Não foi possível importar este arquivo porque este bloco de notas já tinha alterações, consulte como importar para um novo bloco de notas", "pad.impexp.uploadFailed": "O envio falhou. Tente outra vez", "pad.impexp.importfailed": "A importação falhou", "pad.impexp.copypaste": "Copie e cole", diff --git a/src/locales/pt.json b/src/locales/pt.json index c7cfcb5cf..473980fd4 100644 --- a/src/locales/pt.json +++ b/src/locales/pt.json @@ -6,7 +6,8 @@ "Tuliouel", "Waldir", "Imperadeiro98", - "Macofe" + "Macofe", + "Ti4goc" ] }, "index.newPad": "Nova Nota", @@ -105,6 +106,7 @@ "pad.impexp.importbutton": "Importar agora", "pad.impexp.importing": "Importando...", "pad.impexp.confirmimport": "A importação de um ficheiro irá substituir o texto atual do pad. Tem certeza que deseja continuar?", + "pad.impexp.padHasData": "Não fomos capazes de importar este ficheiro porque esta Almofada já tinha alterações, consulte importar para um novo bloco", "pad.impexp.uploadFailed": "O upload falhou. Por favor, tente novamente", "pad.impexp.importfailed": "A importação falhou", "pad.impexp.copypaste": "Por favor, copie e cole" diff --git a/src/locales/sv.json b/src/locales/sv.json index bda3cb83b..a53146bf6 100644 --- a/src/locales/sv.json +++ b/src/locales/sv.json @@ -28,6 +28,7 @@ "pad.colorpicker.save": "Spara", "pad.colorpicker.cancel": "Avbryt", "pad.loading": "Läser in...", + "pad.noCookie": "Kunde inte hitta några kakor. Var god tillåt kakor i din webbläsare!", "pad.passwordRequired": "Du behöver ett lösenord för att få tillgång till detta block", "pad.permissionDenied": "Du har inte åtkomstbehörighet för detta block", "pad.wrongPassword": "Ditt lösenord var fel", @@ -46,6 +47,7 @@ "pad.importExport.import": "Ladda upp textfiler eller dokument", "pad.importExport.importSuccessful": "Åtgärden slutfördes!", "pad.importExport.export": "Export aktuellt block som:", + "pad.importExport.exportetherpad": "Etherpad", "pad.importExport.exporthtml": "HTML", "pad.importExport.exportplain": "Oformaterad text", "pad.importExport.exportword": "Microsoft Word", @@ -116,6 +118,7 @@ "pad.impexp.importing": "Importerar...", "pad.impexp.confirmimport": "Att importera en fil kommer att skriva över den aktuella texten i blocket. Är du säker på att du vill fortsätta?", "pad.impexp.convertFailed": "Vi kunde inte importera denna fil. Var god använd ett annat dokumentformat eller kopiera och klistra in den manuellt", + "pad.impexp.padHasData": "Vi kunde inte importera denna fil eftersom detta block redan har redigerats. Importera den till ett nytt block.", "pad.impexp.uploadFailed": "Uppladdningen misslyckades, var god försök igen", "pad.impexp.importfailed": "Importering misslyckades", "pad.impexp.copypaste": "Var god kopiera och klistra in", diff --git a/src/locales/zh-hans.json b/src/locales/zh-hans.json index 104e850b3..bc1c97b5a 100644 --- a/src/locales/zh-hans.json +++ b/src/locales/zh-hans.json @@ -24,7 +24,7 @@ "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.redo.title": "重做(Ctrl+Y)", "pad.toolbar.clearAuthorship.title": "清除作者颜色(Ctrl+Shift+C)", "pad.toolbar.import_export.title": "从不同的文件格式导入/导出", "pad.toolbar.timeslider.title": "时间轴", @@ -35,6 +35,7 @@ "pad.colorpicker.save": "保存", "pad.colorpicker.cancel": "取消", "pad.loading": "载入中……", + "pad.noCookie": "无法找到Cookie。请在您的浏览器中允许Cookie!", "pad.passwordRequired": "您需要密码才能访问这个记事本", "pad.permissionDenied": "您没有访问这个记事本的权限", "pad.wrongPassword": "您的密码错了", @@ -53,6 +54,7 @@ "pad.importExport.import": "上载任何文本文件或档案", "pad.importExport.importSuccessful": "成功!", "pad.importExport.export": "当前记事本导出为:", + "pad.importExport.exportetherpad": "Etherpad", "pad.importExport.exporthtml": "HTML", "pad.importExport.exportplain": "纯文本", "pad.importExport.exportword": "Microsoft Word", @@ -123,6 +125,7 @@ "pad.impexp.importing": "正在导入...", "pad.impexp.confirmimport": "导入的文件将覆盖记事本的当前文本。你确定要继续吗?", "pad.impexp.convertFailed": "我们无法导入此文档。请使用他文档格式或手动复制贴上。", + "pad.impexp.padHasData": "我们无法导入此文件,因为此记事本已经变更,请导入到一个新的记事本", "pad.impexp.uploadFailed": "上载失败,请重试", "pad.impexp.importfailed": "导入失败", "pad.impexp.copypaste": "请复制粘贴", diff --git a/src/locales/zh-hant.json b/src/locales/zh-hant.json index 6d79268c2..a692d0ca4 100644 --- a/src/locales/zh-hant.json +++ b/src/locales/zh-hant.json @@ -96,13 +96,13 @@ "timeslider.saved": "{{year}}年{{month}}{{day}}日儲存", "timeslider.dateformat": "{{year}}年{{month}}月{{day}}日 {{hours}}:{{minutes}}:{{seconds}}", "timeslider.month.january": "1月", - "timeslider.month.february": "2月", + "timeslider.month.february": "二月", "timeslider.month.march": "3月", "timeslider.month.april": "4月", "timeslider.month.may": "5月", "timeslider.month.june": "6月", "timeslider.month.july": "7月", - "timeslider.month.august": "8月", + "timeslider.month.august": "八月", "timeslider.month.september": "9月", "timeslider.month.october": "10月", "timeslider.month.november": "11月", From 44d1d6cc13e1742fd6424e8ecb9cb914ae2e0b83 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 19 Jan 2015 14:32:58 +0000 Subject: [PATCH 269/455] fix ie editing --- src/static/js/domline.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/static/js/domline.js b/src/static/js/domline.js index b1927b162..b8e6eb146 100644 --- a/src/static/js/domline.js +++ b/src/static/js/domline.js @@ -225,7 +225,7 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument) { newHTML += ' '; } - else if (!browser.msie) + else { newHTML += '
      '; } From ab71bc7c205e6944fcdc46b09cb8e603c433540e Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 19 Jan 2015 14:45:49 +0000 Subject: [PATCH 270/455] hrm --- src/static/js/ace2_inner.js | 7 +++++-- src/static/js/contentcollector.js | 2 -- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index bd1f0053f..ebc03ca4f 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -1605,7 +1605,7 @@ function Ace2Inner(){ if (linesWrapped > 0) { - if(!browser.ie){ + if(!browser.msie){ // chrome decides in it's infinite wisdom that its okay to put the browsers visisble window in the middle of the span // an outcome of this is that the first chars of the string are no longer visible to the user.. Yay chrome.. // Move the browsers visible area to the left hand side of the span @@ -2905,6 +2905,7 @@ function Ace2Inner(){ { if (browser.msie && (!nonEmpty)) { +top.console.log("here"); var result = { node: null, appendSpan: noop, @@ -3583,6 +3584,7 @@ function Ace2Inner(){ // On Mac and Linux, move right moves to end of word and move left moves to start; // on Windows, always move to start of word. // On Windows, Firefox and IE disagree on whether to stop for punctuation (FF says no). + /* if (browser.windows && forwardNotBack) { while ((!isDone()) && isWordChar(nextChar())) @@ -3605,6 +3607,7 @@ function Ace2Inner(){ advance(); } } + */ return i; } @@ -4867,7 +4870,7 @@ function Ace2Inner(){ }) // CompositionEvent is not implemented below IE version 8 - if ( !(browser.msie && browser.version < 9) && document.documentElement) + if ( !(browser.msie && parseInt(browser.version) < 9) && document.documentElement) { $(document.documentElement).on("compositionstart", handleCompositionEvent); $(document.documentElement).on("compositionend", handleCompositionEvent); diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index b7b3c568b..1759d8656 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -625,13 +625,11 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class _ensureColumnZero(state); } } - if (browser.msie) { // in IE, a point immediately after a DIV appears on the next line _reachBlockPoint(node, 1, state); } - state.localAttribs = localAttribs; }; // can pass a falsy value for end of doc From c315defc3124b44e9c5300e93952f124f7e1d82f Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 19 Jan 2015 15:15:52 +0000 Subject: [PATCH 271/455] temp bodge fix --- 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 ebc03ca4f..5204e2a19 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -1910,6 +1910,7 @@ function Ace2Inner(){ if (charsLeft === 0) { var index = 0; + browser.msie = false; // Temp fix to resolve enter and backspace issues.. if (browser.msie && line == (rep.lines.length() - 1) && lineNode.childNodes.length === 0) { // best to stay at end of last empty div in IE @@ -2905,7 +2906,6 @@ function Ace2Inner(){ { if (browser.msie && (!nonEmpty)) { -top.console.log("here"); var result = { node: null, appendSpan: noop, From 5967e085b7bc7efe8ab9ab1be8c728763b3f876a Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 19 Jan 2015 15:37:29 +0000 Subject: [PATCH 272/455] fix ul tests --- tests/backend/specs/api/pad.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/backend/specs/api/pad.js b/tests/backend/specs/api/pad.js index 49800e6fc..3527f95ad 100644 --- a/tests/backend/specs/api/pad.js +++ b/tests/backend/specs/api/pad.js @@ -12,7 +12,7 @@ var apiVersion = 1; var testPadId = makeid(); var lastEdited = ""; var text = generateLongText(); -var ULhtml = '
      • one
      • 2

        • UL2
      '; +var ULhtml = '
      • one
      • 2

        • UL2
      '; describe('Connectivity', function(){ it('errors if can not connect', function(done) { @@ -450,8 +450,9 @@ describe('getHTML', function(){ it('Gets the HTML of a Pad with a bunch of weird unordered lists inserted', function(done) { api.get(endPoint('getHTML')+"&padID="+testPadId) .expect(function(res){ - console.log("foo", res.body.data.html); - if(res.body.data !== ULhtml) throw new Error("Imported HTML does not match served HTML") + var ehtml = res.body.data.html.replace("
      ", "").toLowerCase(); + var uhtml = ULhtml.toLowerCase(); + if(ehtml !== uhtml) throw new Error("Imported HTML does not match served HTML") }) .expect('Content-Type', /json/) .expect(200, done) From 860c584b425b88ba504e4cbe50c2420a375987aa Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 19 Jan 2015 15:44:16 +0000 Subject: [PATCH 273/455] remove pointless comments --- tests/backend/specs/api/pad.js | 30 +----------------------------- 1 file changed, 1 insertion(+), 29 deletions(-) diff --git a/tests/backend/specs/api/pad.js b/tests/backend/specs/api/pad.js index 3527f95ad..6010a11ce 100644 --- a/tests/backend/specs/api/pad.js +++ b/tests/backend/specs/api/pad.js @@ -399,6 +399,7 @@ describe('setHTML', function(){ var html = "
      Hello HTML
      "; api.get(endPoint('setHTML')+"&padID="+testPadId+"&html="+html) .expect(function(res){ +console.log(res.body.code); if(res.body.code !== 1) throw new Error("Allowing crappy HTML to be imported") }) .expect('Content-Type', /json/) @@ -418,35 +419,6 @@ describe('setHTML', function(){ }) describe('getHTML', function(){ - // will fail due to https://github.com/ether/etherpad-lite/issues/1604 - // reminder to self this is how the HTML looks - //
        - //
      • one
      • - //
      • 2
      • - //
      - //
      - //
        - //
          - //
        • UL2
        • - //
        - //
      - // It will look right in the browser but the export will get it horriby wrong - - // This is what the export puts out - // - //
        - //
      • one
      • - //
      • 2
      • - //
      - //
      - //
        - //
          - //
        • UL2
        • - //
        - //
      - //
      - // - it('Gets the HTML of a Pad with a bunch of weird unordered lists inserted', function(done) { api.get(endPoint('getHTML')+"&padID="+testPadId) .expect(function(res){ From b8af62fdef791e3709a2ebc0443046a4416e1b42 Mon Sep 17 00:00:00 2001 From: Gabriel Liwerant Date: Tue, 20 Jan 2015 10:53:52 -0500 Subject: [PATCH 274/455] Add subdirectory installation instructions Out of the box, etherpad-lite does not work correctly if cloned/installed into a subdirectory within an existing project. To do so, a few minor tweaks to the installation process are necessary which are documented here. --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 0cddb0b0f..63916ba77 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,12 @@ Now, run `start.bat` and open in your browser. Update to the latest version with `git pull origin`, then run `bin\installOnWindows.bat`, again. +If cloning to a subdirectory within another project, you may need to do the following: + +1. Start the server manually (e.g. `node/node_modules/ep_etherpad-lite/node/server.js]`) +2. Edit the db `filename` in `settings.json` to the relative directory with the file (e.g. `application/lib/etherpad-lite/var/dirty.db`) +3. Add auto-generated files to the main project `.gitignore` + [Next steps](#next-steps). ## GNU/Linux and other UNIX-like systems From 531d25ab84d34f89e9b147ffd3f2628a90ab6c8a Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 20 Jan 2015 23:50:55 +0000 Subject: [PATCH 275/455] freeze deps --- src/package.json | 65 ++++++++++++++++++++++++------------------------ 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/src/package.json b/src/package.json index 97a2dd6ae..9259b50f6 100644 --- a/src/package.json +++ b/src/package.json @@ -1,48 +1,49 @@ { "name" : "ep_etherpad-lite", "description" : "A Etherpad based on node.js", - "homepage" : "https://github.com/ether/etherpad-lite", + "homepage" : "http://etherpad.org", "keywords" : ["etherpad", "realtime", "collaborative", "editor"], - "author" : "Peter 'Pita' Martischka - Primary Technology Ltd", + "author" : "Etherpad Foundation", "contributors" : [ { "name": "John McLear" }, { "name": "Hans Pinckaers" }, { "name": "Robin Buse" }, - { "name": "Marcel Klehr" } + { "name": "Marcel Klehr" }, + { "name": "Peter Martischka" } ], "dependencies" : { - "etherpad-yajsml" : ">=0.0.2", - "request" : ">=2.48.0", - "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", - "connect" : "2.7.x", + "etherpad-yajsml" : "0.0.2", + "request" : "2.51.0", + "etherpad-require-kernel" : "1.0.7", + "resolve" : "1.0.0", + "socket.io" : "1.3.2", + "ueberDB" : "0.2.10", + "express" : "3.8.1", + "async" : "0.9.0", + "connect" : "2.7.11", "clean-css" : "0.3.2", - "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" : ">=2.1.x", - "ejs" : "~1.0.0", - "graceful-fs" : ">=3.0.4", - "slide" : ">=1.1.6", - "semver" : ">=2.3.0", + "uglify-js" : "2.4.16", + "formidable" : "1.0.16", + "log4js" : "0.6.22", + "nodemailer" : "0.3.44", + "cheerio" : "0.18.0", + "async-stacktrace" : "0.0.2", + "npm" : "2.2.0", + "ejs" : "1.0.0", + "graceful-fs" : "3.0.5", + "slide" : "1.1.6", + "semver" : "4.2.0", "security" : "1.0.0", - "tinycon" : ">=0.0.1", + "tinycon" : "0.0.1", "underscore" : "1.5.1", - "unorm" : ">=1.3.3", - "languages4translatewiki" : ">=0.1.3", - "swagger-node-express" : ">=2.1.0", - "channels" : "0.0.x", - "jsonminify" : ">=0.2.3", - "measured" : ">=0.1.6", - "mocha" : ">=2.0.1", - "supertest" : ">=0.15.0" + "unorm" : "1.3.3", + "languages4translatewiki" : "0.1.3", + "swagger-node-express" : "2.1.3", + "channels" : "0.0.4", + "jsonminify" : "0.2.3", + "measured" : "0.1.6", + "mocha" : "2.1.0", + "supertest" : "0.15.0" }, "bin": { "etherpad-lite": "./node/server.js" }, "devDependencies": { From 5f9bbd0666026253beb4eb6e8f95d54fcbc573d0 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 21 Jan 2015 00:03:39 +0000 Subject: [PATCH 276/455] restrict expor4t to only expose this pad ID --- src/node/utils/ExportEtherpad.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/node/utils/ExportEtherpad.js b/src/node/utils/ExportEtherpad.js index 36df452da..4f91e4e39 100644 --- a/src/node/utils/ExportEtherpad.js +++ b/src/node/utils/ExportEtherpad.js @@ -48,6 +48,7 @@ exports.getPadRaw = function(padId, callback){ // Get the author info db.get("globalAuthor:"+authorId, function(e, authorEntry){ + authorEntry.padIDs = padId; if(!e) data["globalAuthor:"+authorId] = authorEntry; }); From cb6522499734afe156d1e5b660858d93645fb8c6 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 21 Jan 2015 00:28:00 +0000 Subject: [PATCH 277/455] better logic for author handling --- src/node/utils/ImportEtherpad.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/node/utils/ImportEtherpad.js b/src/node/utils/ImportEtherpad.js index 8daeb5363..1574a3a9d 100644 --- a/src/node/utils/ImportEtherpad.js +++ b/src/node/utils/ImportEtherpad.js @@ -24,12 +24,25 @@ exports.setPadRaw = function(padId, records, callback){ async.eachSeries(Object.keys(records), function(key, cb){ var value = records[key] - // we know its an author + // Author data if(value.padIDs){ // rewrite author pad ids value.padIDs[padId] = 1; var newKey = key; + // Does this author already exist? + db.get(key, function(err, author){ + if(author){ + // Yes, add the padID to the author.. + author.padIDs.push(padId); + value = author; + }else{ + // No, create a new array with the author info in + value.padIDs = [padId]; + } + }); + + // Not author data, probably pad data }else{ // we can split it to look to see if its pad data var oldPadId = key.split(":"); From cbeb464358c231b204751e580184365e0a1467f1 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 21 Jan 2015 01:16:12 +0000 Subject: [PATCH 278/455] ie now uses keydown --- tests/frontend/specs/bold.js | 3 +-- tests/frontend/specs/caret.js | 2 +- tests/frontend/specs/indentation.js | 2 +- tests/frontend/specs/italic.js | 2 +- tests/frontend/specs/redo.js | 2 +- 5 files changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/frontend/specs/bold.js b/tests/frontend/specs/bold.js index 703d0815a..bffb281b5 100644 --- a/tests/frontend/specs/bold.js +++ b/tests/frontend/specs/bold.js @@ -43,9 +43,8 @@ describe("bold button", function(){ //select this text element $firstTextElement.sendkeys('{selectall}'); - console.log(inner$(window)[0].bowser); - if(inner$(window)[0].bowser.firefox){ // if it's a mozilla browser + if(inner$(window)[0].bowser.firefox || inner$(window)[0].bowser.msie){ // if it's a mozilla or IE var evtType = "keypress"; }else{ var evtType = "keydown"; diff --git a/tests/frontend/specs/caret.js b/tests/frontend/specs/caret.js index 48dd94a66..6e68b6f52 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.firefox){ // if it's a mozilla browser + if(inner$(window)[0].bowser.firefox || inner$(window)[0].bowser.msie){ // if it's a mozilla or IE var evtType = "keypress"; }else{ var evtType = "keydown"; diff --git a/tests/frontend/specs/indentation.js b/tests/frontend/specs/indentation.js index 3730f6bf5..c8f2cee81 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.firefox){ // if it's a mozilla browser + if(inner$(window)[0].bowser.firefox || inner$(window)[0].bowser.msie){ // if it's a mozilla or IE var evtType = "keypress"; }else{ var evtType = "keydown"; diff --git a/tests/frontend/specs/italic.js b/tests/frontend/specs/italic.js index 955cec0fd..4ef4b4085 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.firefox){ // if it's a mozilla browser + if(inner$(window)[0].bowser.firefox || inner$(window)[0].bowser.msie){ // if it's a mozilla or IE var evtType = "keypress"; }else{ var evtType = "keydown"; diff --git a/tests/frontend/specs/redo.js b/tests/frontend/specs/redo.js index 99056fb75..00873d14a 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.firefox){ // if it's a mozilla browser + if(inner$(window)[0].bowser.firefox || inner$(window)[0].bowser.msie){ // if it's a mozilla or IE var evtType = "keypress"; }else{ var evtType = "keydown"; From eacf1118f86fa0bf489fa86f65b970347f45077a Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 21 Jan 2015 02:55:03 +0000 Subject: [PATCH 279/455] blind attempt to try --- src/static/js/ace2_inner.js | 20 ++++++++------------ src/static/js/contentcollector.js | 3 ++- src/static/js/domline.js | 4 ++-- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index bd1f0053f..d2952ffe3 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -34,8 +34,9 @@ var isNodeText = Ace2Common.isNodeText, binarySearchInfinite = Ace2Common.binarySearchInfinite, htmlPrettyEscape = Ace2Common.htmlPrettyEscape, noop = Ace2Common.noop; - var hooks = require('./pluginfw/hooks'); - var browser = require('./browser').browser; + +var hooks = require('./pluginfw/hooks'); +var browser = require('./browser').browser; function Ace2Inner(){ @@ -944,7 +945,7 @@ function Ace2Inner(){ showslinenumbers : function(value){ hasLineNumbers = !! value; // disable line numbers on mobile devices - if (browser.mobile) hasLineNumbers = false; + // if (browser.mobile) hasLineNumbers = false; setClassPresence(sideDiv, "sidedivhidden", !hasLineNumbers); fixView(); }, @@ -1605,7 +1606,7 @@ function Ace2Inner(){ if (linesWrapped > 0) { - if(!browser.ie){ + if(!browser.msie){ // chrome decides in it's infinite wisdom that its okay to put the browsers visisble window in the middle of the span // an outcome of this is that the first chars of the string are no longer visible to the user.. Yay chrome.. // Move the browsers visible area to the left hand side of the span @@ -2964,6 +2965,7 @@ function Ace2Inner(){ } else { + // cake return domline.createDomLine(nonEmpty, doesWrap, browser, doc); } } @@ -3583,7 +3585,7 @@ function Ace2Inner(){ // On Mac and Linux, move right moves to end of word and move left moves to start; // on Windows, always move to start of word. // On Windows, Firefox and IE disagree on whether to stop for punctuation (FF says no). - if (browser.windows && forwardNotBack) + if (browser.msie && forwardNotBack) { while ((!isDone()) && isWordChar(nextChar())) { @@ -4264,12 +4266,6 @@ function Ace2Inner(){ end.collapse(false); selection.startPoint = pointFromCollapsedRange(start); selection.endPoint = pointFromCollapsedRange(end); -/*if ((!selection.startPoint.node.isText) && (!selection.endPoint.node.isText)) { - console.log(selection.startPoint.node.uniqueId()+","+ - selection.startPoint.index+" / "+ - selection.endPoint.node.uniqueId()+","+ - selection.endPoint.index); -}*/ } return selection; } @@ -4867,7 +4863,7 @@ function Ace2Inner(){ }) // CompositionEvent is not implemented below IE version 8 - if ( !(browser.msie && browser.version < 9) && document.documentElement) + if ( !(browser.msie && browser.version <= 9) && document.documentElement) { $(document.documentElement).on("compositionstart", handleCompositionEvent); $(document.documentElement).on("compositionend", handleCompositionEvent); diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index b7b3c568b..7a6deeb11 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -37,7 +37,8 @@ function sanitizeUnicode(s) function makeContentCollector(collectStyles, browser, apool, domInterface, className2Author) { - browser = browser || {}; + // browser = browser || {}; + // I don't like the above. var dom = domInterface || { isNodeText: function(n) diff --git a/src/static/js/domline.js b/src/static/js/domline.js index b1927b162..e793956b5 100644 --- a/src/static/js/domline.js +++ b/src/static/js/domline.js @@ -65,7 +65,7 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument) lineMarker: 0 }; - var browser = (optBrowser || {}); + // var browser = (optBrowser || {}); var document = optDocument; if (document) @@ -225,7 +225,7 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument) { newHTML += ' '; } - else if (!browser.msie) + else if (!optBrowser.msie) { newHTML += '
      '; } From f880b0feb7df9c05d78f4c320ece1b846f4fe27a Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 21 Jan 2015 12:18:38 +0000 Subject: [PATCH 280/455] clean up iojs --- bin/installDeps.sh | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/bin/installDeps.sh b/bin/installDeps.sh index 005758451..fcf213e48 100755 --- a/bin/installDeps.sh +++ b/bin/installDeps.sh @@ -10,29 +10,29 @@ fi #Is gnu-grep (ggrep) installed on SunOS (Solaris) if [ $(uname) = "SunOS" ]; then - hash ggrep > /dev/null 2>&1 || { + hash ggrep > /dev/null 2>&1 || { echo "Please install ggrep (pkg install gnu-grep)" >&2 - exit 1 + exit 1 } fi #Is curl installed? -hash curl > /dev/null 2>&1 || { +hash curl > /dev/null 2>&1 || { echo "Please install curl" >&2 - exit 1 + exit 1 } #Is node installed? #not checking io.js, default installation creates a symbolic link to node -hash node > /dev/null 2>&1 || { +hash node > /dev/null 2>&1 || { echo "Please install node.js ( http://nodejs.org )" >&2 - exit 1 + exit 1 } #Is npm installed? -hash npm > /dev/null 2>&1 || { +hash npm > /dev/null 2>&1 || { echo "Please install npm ( http://npmjs.org )" >&2 - exit 1 + exit 1 } #check npm version @@ -40,18 +40,20 @@ NPM_VERSION=$(npm --version) 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 + exit 1 fi #check node version NODE_VERSION=$(node --version) NODE_V_MINOR=$(echo $NODE_VERSION | cut -d "." -f 1-2) #iojs version checking added -IOJS_VERSION=$(iojs --version) +if hash iojs 2>/dev/null; then + IOJS_VERSION=$(iojs --version) +fi if [ ! $NODE_V_MINOR = "v0.8" ] && [ ! $NODE_V_MINOR = "v0.10" ] && [ ! $NODE_V_MINOR = "v0.11" ]; then if [ ! $IOJS_VERSION ]; then echo "You're running a wrong version of node, or io.js is not installed. You're using $NODE_VERSION, we need v0.8.x, v0.10.x or v0.11.x" >&2 - exit 1 + exit 1 fi fi @@ -76,9 +78,9 @@ echo "Ensure that all dependencies are up to date... If this is the first time [ -e ep_etherpad-lite ] || ln -s ../src ep_etherpad-lite cd ep_etherpad-lite npm install --loglevel warn -) || { +) || { rm -rf node_modules - exit 1 + exit 1 } echo "Ensure jQuery is downloaded and up to date..." @@ -111,7 +113,7 @@ do if [ ! -f "src/static/custom/$f.js" ]; then cp "src/static/custom/js.template" "src/static/custom/$f.js" || exit 1 fi - + if [ ! -f "src/static/custom/$f.css" ]; then cp "src/static/custom/css.template" "src/static/custom/$f.css" || exit 1 fi From 34d6b31c983475e7bf222a593e937fdee796788d Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 21 Jan 2015 13:24:21 +0000 Subject: [PATCH 281/455] some sanity --- src/static/js/ace2_inner.js | 93 ++++++++++++++++------------------ src/static/js/broadcast.js | 12 ----- src/static/js/collab_client.js | 2 +- src/static/js/pad.js | 3 +- 4 files changed, 47 insertions(+), 63 deletions(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index d2952ffe3..8adbc8dcd 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -19,8 +19,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -var _, $, jQuery, plugins, Ace2Common; - +var _, $, jQuery, plugins, Ace2Common, mybrowser; +mybrowser = require('./browser').browser; Ace2Common = require('./ace2_common'); plugins = require('ep_etherpad-lite/static/js/pluginfw/client_plugins'); @@ -36,7 +36,6 @@ var isNodeText = Ace2Common.isNodeText, noop = Ace2Common.noop; var hooks = require('./pluginfw/hooks'); -var browser = require('./browser').browser; function Ace2Inner(){ @@ -599,7 +598,7 @@ function Ace2Inner(){ // Chrome can't handle the truth.. If CSS rule white-space:pre-wrap // is true then any paste event will insert two lines.. - if(browser.chrome){ + if(mybrowser.chrome){ $("#innerdocbody").css({"white-space":"normal"}); } @@ -945,7 +944,7 @@ function Ace2Inner(){ showslinenumbers : function(value){ hasLineNumbers = !! value; // disable line numbers on mobile devices - // if (browser.mobile) hasLineNumbers = false; + // if (mybrowser.mobile) hasLineNumbers = false; setClassPresence(sideDiv, "sidedivhidden", !hasLineNumbers); fixView(); }, @@ -1312,7 +1311,9 @@ function Ace2Inner(){ else { var offsetIntoLine = 0; - var filteredFunc = linestylefilter.getFilterStack(text, textAndClassFunc, browser); +mybrowser.msie = false; // cake 1 + var filteredFunc = linestylefilter.getFilterStack(text, textAndClassFunc, mybrowser); +mybrowser.msie = true; var lineNum = rep.lines.indexOfEntry(lineEntry); var aline = rep.alines[lineNum]; filteredFunc = linestylefilter.getLineStyleFilter( @@ -1560,13 +1561,16 @@ function Ace2Inner(){ lastDirtyNode = (lastDirtyNode && isNodeDirty(lastDirtyNode) && lastDirtyNode); if (firstDirtyNode && lastDirtyNode) { - var cc = makeContentCollector(isStyled, browser, rep.apool, null, className2Author); +// cake 2 +mybrowser.msie = false; + var cc = makeContentCollector(isStyled, mybrowser, rep.apool, null, className2Author); +mybrowser.msie = true; cc.notifySelection(selection); var dirtyNodes = []; for (var n = firstDirtyNode; n && !(n.previousSibling && n.previousSibling == lastDirtyNode); n = n.nextSibling) { - if (browser.msie) + if (mybrowser.msie) { // try to undo IE's pesky and overzealous linkification try @@ -1606,7 +1610,7 @@ function Ace2Inner(){ if (linesWrapped > 0) { - if(!browser.msie){ + if(!mybrowser.msie){ // chrome decides in it's infinite wisdom that its okay to put the browsers visisble window in the middle of the span // an outcome of this is that the first chars of the string are no longer visible to the user.. Yay chrome.. // Move the browsers visible area to the left hand side of the span @@ -1911,7 +1915,7 @@ function Ace2Inner(){ if (charsLeft === 0) { var index = 0; - if (browser.msie && line == (rep.lines.length() - 1) && lineNode.childNodes.length === 0) + if (mybrowser.msie && line == (rep.lines.length() - 1) && lineNode.childNodes.length === 0) { // best to stay at end of last empty div in IE index = 1; @@ -2904,7 +2908,7 @@ function Ace2Inner(){ function doCreateDomLine(nonEmpty) { - if (browser.msie && (!nonEmpty)) + if (mybrowser.msie && (!nonEmpty)) { var result = { node: null, @@ -2965,8 +2969,10 @@ function Ace2Inner(){ } else { - // cake - return domline.createDomLine(nonEmpty, doesWrap, browser, doc); + // cake 3 +mybrowser.msie = false; + return domline.createDomLine(nonEmpty, doesWrap, mybrowser, doc); +mybrowser.msie = true; } } @@ -3233,7 +3239,7 @@ function Ace2Inner(){ var dirtiness = {}; dirtiness.nodeId = uniqueId(n); dirtiness.knownHTML = n.innerHTML; - if (browser.msie) + if (mybrowser.msie) { // adding a space to an "empty" div in IE designMode doesn't // change the innerHTML of the div's parent; also, other @@ -3250,7 +3256,7 @@ function Ace2Inner(){ var data = getAssoc(n, "dirtiness"); if (!data) return true; if (n.id !== data.nodeId) return true; - if (browser.msie) + if (mybrowser.msie) { if (n.innerText !== data.knownText) return true; } @@ -3585,7 +3591,7 @@ function Ace2Inner(){ // On Mac and Linux, move right moves to end of word and move left moves to start; // on Windows, always move to start of word. // On Windows, Firefox and IE disagree on whether to stop for punctuation (FF says no). - if (browser.msie && forwardNotBack) + if (mybrowser.msie && forwardNotBack) { while ((!isDone()) && isWordChar(nextChar())) { @@ -3657,13 +3663,13 @@ function Ace2Inner(){ if (isModKey) return; // If the key is a keypress and the browser is opera and the key is enter, do nothign at all as this fires twice. - if (keyCode == 13 && browser.opera && (type == "keypress")){ + if (keyCode == 13 && mybrowser.opera && (type == "keypress")){ return; // This stops double enters in Opera but double Tabs still show on single tab keypress, adding keyCode == 9 to this doesn't help as the event is fired twice } var specialHandled = false; - var isTypeForSpecialKey = ((browser.msie || browser.safari || browser.chrome) ? (type == "keydown") : (type == "keypress")); - var isTypeForCmdKey = ((browser.msie || browser.safari || browser.chrome) ? (type == "keydown") : (type == "keypress")); + var isTypeForSpecialKey = ((mybrowser.msie || mybrowser.safari || mybrowser.chrome) ? (type == "keydown") : (type == "keypress")); + var isTypeForCmdKey = ((mybrowser.msie || mybrowser.safari || mybrowser.chrome) ? (type == "keydown") : (type == "keypress")); var stopped = false; @@ -3880,7 +3886,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) && mybrowser.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 @@ -3951,10 +3957,10 @@ function Ace2Inner(){ } // Is part of multi-keystroke international character on Firefox Mac - var isFirefoxHalfCharacter = (browser.mozilla && evt.altKey && charCode === 0 && keyCode === 0); + var isFirefoxHalfCharacter = (mybrowser.firefox && evt.altKey && charCode === 0 && keyCode === 0); // Is part of multi-keystroke international character on Safari Mac - var isSafariHalfCharacter = (browser.safari && evt.altKey && keyCode == 229); + var isSafariHalfCharacter = (mybrowser.safari && evt.altKey && keyCode == 229); if (thisKeyDoesntTriggerNormalize || isFirefoxHalfCharacter || isSafariHalfCharacter) { @@ -4068,7 +4074,7 @@ function Ace2Inner(){ // each of which has node (a magicdom node), index, and maxIndex. If the node // is a text node, maxIndex is the length of the text; else maxIndex is 1. // index is between 0 and maxIndex, inclusive. - if (browser.msie) + if (mybrowser.msie) { var browserSelection; try @@ -4364,7 +4370,7 @@ function Ace2Inner(){ maxIndex: pt.maxIndex }; } - if (browser.msie) + if (mybrowser.msie) { // Oddly enough, accessing scrollHeight fixes return key handling on IE 8, // presumably by forcing some kind of internal DOM update. @@ -4653,17 +4659,17 @@ function Ace2Inner(){ for (var i = 0; i < 2; i++) { var newHeight = root.clientHeight; - var newWidth = (browser.msie ? root.createTextRange().boundingWidth : root.clientWidth); + var newWidth = root.clientWidth; var viewHeight = getInnerHeight() - iframePadBottom - iframePadTop; var viewWidth = getInnerWidth() - iframePadLeft - iframePadRight; if (newHeight < viewHeight) { newHeight = viewHeight; - if (browser.msie) setIfNecessary(outerWin.document.documentElement.style, 'overflowY', 'auto'); +// if (mybrowser.msie) setIfNecessary(outerWin.document.documentElement.style, 'overflowY', 'auto'); } else { - if (browser.msie) setIfNecessary(outerWin.document.documentElement.style, 'overflowY', 'scroll'); +// if (mybrowser.msie) setIfNecessary(outerWin.document.documentElement.style, 'overflowY', 'scroll'); } if (doesWrap) { @@ -4677,7 +4683,7 @@ function Ace2Inner(){ setIfNecessary(iframe.style, "width", newWidth + "px"); setIfNecessary(sideDiv.style, "height", newHeight + "px"); } - if (browser.mozilla) + if (mybrowser.firefox) { if (!doesWrap) { @@ -4776,14 +4782,14 @@ function Ace2Inner(){ } return false; } - if (browser.msie || browser.safari) + if (mybrowser.msie || mybrowser.safari) { setIfNecessary(root, 'contentEditable', (newVal ? 'true' : 'false')); } else { var wasSet = setIfNecessary(doc, 'designMode', (newVal ? 'on' : 'off')); - if (wasSet && newVal && browser.opera) + if (wasSet && newVal && mybrowser.opera) { // turning on designMode clears event handlers bindTheEventHandlers(); @@ -4849,11 +4855,11 @@ function Ace2Inner(){ $(document).on("keyup", handleKeyEvent); $(document).on("click", handleClick); $(root).on("blur", handleBlur); - if (browser.msie) + if (mybrowser.msie) { $(document).on("click", handleIEOuterClick); } - if (browser.msie) $(root).on("paste", handleIEPaste); + if (mybrowser.msie) $(root).on("paste", handleIEPaste); // Don't paste on middle click of links $(root).on("paste", function(e){ @@ -4863,7 +4869,7 @@ function Ace2Inner(){ }) // CompositionEvent is not implemented below IE version 8 - if ( !(browser.msie && browser.version <= 9) && document.documentElement) + if ( !(mybrowser.msie && mybrowser.version <= 9) && document.documentElement) { $(document.documentElement).on("compositionstart", handleCompositionEvent); $(document.documentElement).on("compositionend", handleCompositionEvent); @@ -4926,7 +4932,7 @@ function Ace2Inner(){ function handleBlur(evt) { - if (browser.msie) + if (mybrowser.msie) { // a fix: in IE, clicking on a control like a button outside the // iframe can "blur" the editor, causing it to stop getting @@ -4997,7 +5003,7 @@ function Ace2Inner(){ var win = outerWin; var odoc = win.document; var h; - if (browser.opera) h = win.innerHeight; + if (mybrowser.opera) h = win.innerHeight; else h = odoc.documentElement.clientHeight; if (h) return h; @@ -5326,20 +5332,9 @@ function Ace2Inner(){ { var body = doc.getElementById("innerdocbody"); root = body; // defined as a var in scope outside - if (browser.mozilla) $(root).addClass("mozilla"); - if (browser.safari) $(root).addClass("safari"); - if (browser.msie) $(root).addClass("msie"); - if (browser.msie) - { - // cache CSS background images - try - { - doc.execCommand("BackgroundImageCache", false, true); - } - catch (e) - { /* throws an error in some IE 6 but not others! */ - } - } + if (mybrowser.firefox) $(root).addClass("mozilla"); + if (mybrowser.safari) $(root).addClass("safari"); + if (mybrowser.msie) $(root).addClass("msie"); setClassPresence(root, "authorColors", true); setClassPresence(root, "doesWrap", doesWrap); diff --git a/src/static/js/broadcast.js b/src/static/js/broadcast.js index a25d889b9..817155b55 100644 --- a/src/static/js/broadcast.js +++ b/src/static/js/broadcast.js @@ -66,18 +66,6 @@ function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro } } - // for IE - if (browser.msie) - { - try - { - document.execCommand("BackgroundImageCache", false, true); - } - catch (e) - {} - } - - //var socket; var channelState = "DISCONNECTED"; diff --git a/src/static/js/collab_client.js b/src/static/js/collab_client.js index 6089f1972..8bc5020b1 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.firefox) { // 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/pad.js b/src/static/js/pad.js index ff62f86cc..685b420b1 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.js @@ -494,7 +494,8 @@ var pad = { pad.initTime = +(new Date()); pad.padOptions = clientVars.initialOptions; - if ((!browser.msie) && (!(browser.mozilla && browser.version.indexOf("1.8.") == 0))) +console.log("pad", browser); + if ((!browser.msie) && (!(browser.firefox && browser.version.indexOf("1.8.") == 0))) { document.domain = document.domain; // for comet } From 990e14c9042cacd03ff16259b9140a28269678c9 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 21 Jan 2015 14:25:24 +0000 Subject: [PATCH 282/455] working with a big hack --- src/static/js/ace2_inner.js | 32 +++++++++++++++---------------- src/static/js/collab_client.js | 1 - src/static/js/contentcollector.js | 10 +++++----- src/static/js/domline.js | 3 ++- src/static/js/linestylefilter.js | 6 +++--- 5 files changed, 25 insertions(+), 27 deletions(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 8adbc8dcd..aa65ff958 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -21,6 +21,15 @@ */ var _, $, jQuery, plugins, Ace2Common, mybrowser; mybrowser = require('./browser').browser; + +if(mybrowser.msie){ + // Honestly fuck IE royally. + // Basically every hack we have since V11 causes a problem + if(parseInt(mybrowser.version) >= 11){ + delete mybrowser.msie; + mybrowser.firefox = true; + } +} Ace2Common = require('./ace2_common'); plugins = require('ep_etherpad-lite/static/js/pluginfw/client_plugins'); @@ -1311,9 +1320,7 @@ function Ace2Inner(){ else { var offsetIntoLine = 0; -mybrowser.msie = false; // cake 1 var filteredFunc = linestylefilter.getFilterStack(text, textAndClassFunc, mybrowser); -mybrowser.msie = true; var lineNum = rep.lines.indexOfEntry(lineEntry); var aline = rep.alines[lineNum]; filteredFunc = linestylefilter.getLineStyleFilter( @@ -1355,7 +1362,7 @@ mybrowser.msie = true; // (from how it looks in our representation) and record them in a way // that can be used to "normalize" the document (apply the changes to our // representation, and put the DOM in a canonical form). - //top.console.log("observeChangesAroundNode(%o)", node); + // top.console.log("observeChangesAroundNode(%o)", node); var cleanNode; var hasAdjacentDirtyness; if (!isNodeDirty(node)) @@ -1561,10 +1568,7 @@ mybrowser.msie = true; lastDirtyNode = (lastDirtyNode && isNodeDirty(lastDirtyNode) && lastDirtyNode); if (firstDirtyNode && lastDirtyNode) { -// cake 2 -mybrowser.msie = false; var cc = makeContentCollector(isStyled, mybrowser, rep.apool, null, className2Author); -mybrowser.msie = true; cc.notifySelection(selection); var dirtyNodes = []; for (var n = firstDirtyNode; n && !(n.previousSibling && n.previousSibling == lastDirtyNode); @@ -2964,15 +2968,11 @@ mybrowser.msie = true; { return ""; }; - return result; } else { - // cake 3 -mybrowser.msie = false; return domline.createDomLine(nonEmpty, doesWrap, mybrowser, doc); -mybrowser.msie = true; } } @@ -3632,7 +3632,6 @@ mybrowser.msie = true; evt.preventDefault(); 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 @@ -3669,8 +3668,7 @@ mybrowser.msie = true; var specialHandled = false; var isTypeForSpecialKey = ((mybrowser.msie || mybrowser.safari || mybrowser.chrome) ? (type == "keydown") : (type == "keypress")); - var isTypeForCmdKey = ((mybrowser.msie || mybrowser.safari || mybrowser.chrome) ? (type == "keydown") : (type == "keypress")); - + var isTypeForCmdKey = (type === "keydown") var stopped = false; inCallStackIfNecessary("handleKeyEvent", function() @@ -3920,10 +3918,10 @@ mybrowser.msie = true; // only move the viewport if we're at the bottom of the viewport, if we hit down any other time the viewport shouldn't change // NOTE: This behavior only fires if Chrome decides to break the page layout after a paste, it's annoying but nothing I can do var selection = getSelection(); - top.console.log("line #", rep.selStart[0]); // the line our caret is on - top.console.log("firstvisible", visibleLineRange[0]); // the first visiblel ine - top.console.log("lastVisible", visibleLineRange[1]); // the last visible line - top.console.log(rep.selStart[0], visibleLineRange[1], rep.selStart[0], visibleLineRange[0]); + // top.console.log("line #", rep.selStart[0]); // the line our caret is on + // top.console.log("firstvisible", visibleLineRange[0]); // the first visiblel ine + // top.console.log("lastVisible", visibleLineRange[1]); // the last visible line + // top.console.log(rep.selStart[0], visibleLineRange[1], rep.selStart[0], visibleLineRange[0]); var newY = viewport.top + lineHeight; } if(newY){ diff --git a/src/static/js/collab_client.js b/src/static/js/collab_client.js index 8bc5020b1..e5c0ec8b2 100644 --- a/src/static/js/collab_client.js +++ b/src/static/js/collab_client.js @@ -81,7 +81,6 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options, _pad) onServerMessage: function() {} }; - if (browser.firefox) { // Prevent "escape" from taking effect and canceling a comet connection; diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index 7a6deeb11..c39410b7c 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -35,9 +35,9 @@ function sanitizeUnicode(s) return UNorm.nfc(s); } -function makeContentCollector(collectStyles, browser, apool, domInterface, className2Author) +function makeContentCollector(collectStyles, abrowser, apool, domInterface, className2Author) { - // browser = browser || {}; + abrowser = abrowser || {}; // I don't like the above. var dom = domInterface || { @@ -485,7 +485,7 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class var cls = dom.nodeProp(node, "className"); var isPre = (tname == "pre"); - if ((!isPre) && browser.safari) + if ((!isPre) && abrowser.safari) { isPre = (styl && /\bwhite-space:\s*pre\b/i.exec(styl)); } @@ -611,7 +611,7 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class } } } - if (!browser.msie) + if (!abrowser.msie) { _reachBlockPoint(node, 1, state); } @@ -627,7 +627,7 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class } } - if (browser.msie) + if (abrowser.msie) { // in IE, a point immediately after a DIV appears on the next line _reachBlockPoint(node, 1, state); diff --git a/src/static/js/domline.js b/src/static/js/domline.js index e793956b5..03f1b9c89 100644 --- a/src/static/js/domline.js +++ b/src/static/js/domline.js @@ -65,7 +65,6 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument) lineMarker: 0 }; - // var browser = (optBrowser || {}); var document = optDocument; if (document) @@ -93,8 +92,10 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument) var perTextNodeProcess = (doesWrap ? _.identity : processSpaces); var perHtmlLineProcess = (doesWrap ? processSpaces : _.identity); var lineClass = 'ace-line'; + result.appendSpan = function(txt, cls) { + var processedMarker = false; // Handle lineAttributeMarker, if present if (cls.indexOf(lineAttributeMarker) >= 0) diff --git a/src/static/js/linestylefilter.js b/src/static/js/linestylefilter.js index 675f19d02..17ab993b1 100644 --- a/src/static/js/linestylefilter.js +++ b/src/static/js/linestylefilter.js @@ -318,20 +318,20 @@ linestylefilter.textAndClassFuncSplitter = function(func, splitPointsOpt) return spanHandler; }; -linestylefilter.getFilterStack = function(lineText, textAndClassFunc, browser) +linestylefilter.getFilterStack = function(lineText, textAndClassFunc, abrowser) { var func = linestylefilter.getURLFilter(lineText, textAndClassFunc); var hookFilters = hooks.callAll("aceGetFilterStack", { linestylefilter: linestylefilter, - browser: browser + browser: abrowser }); _.map(hookFilters ,function(hookFilter) { func = hookFilter(lineText, func); }); - if (browser !== undefined && browser.msie) + if (abrowser !== undefined && abrowser.msie) { // IE7+ will take an e-mail address like and linkify it to foo@bar.com. // We then normalize it back to text with no angle brackets. It's weird. So always From ce40cacafaf220e4c075daa79240dd0263aeaa5d Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 21 Jan 2015 14:49:06 +0000 Subject: [PATCH 283/455] mheh looks fixed --- src/static/js/ace2_inner.js | 78 ++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index aa65ff958..dd2f2b124 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -19,15 +19,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -var _, $, jQuery, plugins, Ace2Common, mybrowser; -mybrowser = require('./browser').browser; +var _, $, jQuery, plugins, Ace2Common, bowser; +bowser = require('./browser').browser; -if(mybrowser.msie){ +if(bowser.msie){ // Honestly fuck IE royally. // Basically every hack we have since V11 causes a problem - if(parseInt(mybrowser.version) >= 11){ - delete mybrowser.msie; - mybrowser.firefox = true; + if(parseInt(bowser.version) >= 11){ + delete bowser.msie; + bowser.chrome = true; } } Ace2Common = require('./ace2_common'); @@ -607,7 +607,7 @@ function Ace2Inner(){ // Chrome can't handle the truth.. If CSS rule white-space:pre-wrap // is true then any paste event will insert two lines.. - if(mybrowser.chrome){ + if(bowser.chrome){ $("#innerdocbody").css({"white-space":"normal"}); } @@ -953,7 +953,7 @@ function Ace2Inner(){ showslinenumbers : function(value){ hasLineNumbers = !! value; // disable line numbers on mobile devices - // if (mybrowser.mobile) hasLineNumbers = false; + // if (bowser.mobile) hasLineNumbers = false; setClassPresence(sideDiv, "sidedivhidden", !hasLineNumbers); fixView(); }, @@ -1320,7 +1320,7 @@ function Ace2Inner(){ else { var offsetIntoLine = 0; - var filteredFunc = linestylefilter.getFilterStack(text, textAndClassFunc, mybrowser); + var filteredFunc = linestylefilter.getFilterStack(text, textAndClassFunc, bowser); var lineNum = rep.lines.indexOfEntry(lineEntry); var aline = rep.alines[lineNum]; filteredFunc = linestylefilter.getLineStyleFilter( @@ -1568,13 +1568,13 @@ function Ace2Inner(){ lastDirtyNode = (lastDirtyNode && isNodeDirty(lastDirtyNode) && lastDirtyNode); if (firstDirtyNode && lastDirtyNode) { - var cc = makeContentCollector(isStyled, mybrowser, rep.apool, null, className2Author); + var cc = makeContentCollector(isStyled, bowser, rep.apool, null, className2Author); cc.notifySelection(selection); var dirtyNodes = []; for (var n = firstDirtyNode; n && !(n.previousSibling && n.previousSibling == lastDirtyNode); n = n.nextSibling) { - if (mybrowser.msie) + if (bowser.msie) { // try to undo IE's pesky and overzealous linkification try @@ -1614,7 +1614,7 @@ function Ace2Inner(){ if (linesWrapped > 0) { - if(!mybrowser.msie){ + if(!bowser.msie){ // chrome decides in it's infinite wisdom that its okay to put the browsers visisble window in the middle of the span // an outcome of this is that the first chars of the string are no longer visible to the user.. Yay chrome.. // Move the browsers visible area to the left hand side of the span @@ -1919,7 +1919,7 @@ function Ace2Inner(){ if (charsLeft === 0) { var index = 0; - if (mybrowser.msie && line == (rep.lines.length() - 1) && lineNode.childNodes.length === 0) + if (bowser.msie && line == (rep.lines.length() - 1) && lineNode.childNodes.length === 0) { // best to stay at end of last empty div in IE index = 1; @@ -2912,7 +2912,7 @@ function Ace2Inner(){ function doCreateDomLine(nonEmpty) { - if (mybrowser.msie && (!nonEmpty)) + if (bowser.msie && (!nonEmpty)) { var result = { node: null, @@ -2972,7 +2972,7 @@ function Ace2Inner(){ } else { - return domline.createDomLine(nonEmpty, doesWrap, mybrowser, doc); + return domline.createDomLine(nonEmpty, doesWrap, bowser, doc); } } @@ -3239,7 +3239,7 @@ function Ace2Inner(){ var dirtiness = {}; dirtiness.nodeId = uniqueId(n); dirtiness.knownHTML = n.innerHTML; - if (mybrowser.msie) + if (bowser.msie) { // adding a space to an "empty" div in IE designMode doesn't // change the innerHTML of the div's parent; also, other @@ -3256,7 +3256,7 @@ function Ace2Inner(){ var data = getAssoc(n, "dirtiness"); if (!data) return true; if (n.id !== data.nodeId) return true; - if (mybrowser.msie) + if (bowser.msie) { if (n.innerText !== data.knownText) return true; } @@ -3591,7 +3591,7 @@ function Ace2Inner(){ // On Mac and Linux, move right moves to end of word and move left moves to start; // on Windows, always move to start of word. // On Windows, Firefox and IE disagree on whether to stop for punctuation (FF says no). - if (mybrowser.msie && forwardNotBack) + if (bowser.msie && forwardNotBack) { while ((!isDone()) && isWordChar(nextChar())) { @@ -3662,13 +3662,13 @@ function Ace2Inner(){ if (isModKey) return; // If the key is a keypress and the browser is opera and the key is enter, do nothign at all as this fires twice. - if (keyCode == 13 && mybrowser.opera && (type == "keypress")){ + if (keyCode == 13 && bowser.opera && (type == "keypress")){ return; // This stops double enters in Opera but double Tabs still show on single tab keypress, adding keyCode == 9 to this doesn't help as the event is fired twice } var specialHandled = false; - var isTypeForSpecialKey = ((mybrowser.msie || mybrowser.safari || mybrowser.chrome) ? (type == "keydown") : (type == "keypress")); - var isTypeForCmdKey = (type === "keydown") + var isTypeForSpecialKey = ((bowser.msie || bowser.safari || bowser.chrome) ? (type == "keydown") : (type == "keypress")); + var isTypeForCmdKey = ((bowser.msie || bowser.safari || bowser.chrome) ? (type == "keydown") : (type == "keypress")); var stopped = false; inCallStackIfNecessary("handleKeyEvent", function() @@ -3884,7 +3884,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) && mybrowser.chrome){ + if((evt.which == 37 || evt.which == 38 || evt.which == 39 || evt.which == 40) && bowser.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 @@ -3955,10 +3955,10 @@ function Ace2Inner(){ } // Is part of multi-keystroke international character on Firefox Mac - var isFirefoxHalfCharacter = (mybrowser.firefox && evt.altKey && charCode === 0 && keyCode === 0); + var isFirefoxHalfCharacter = (bowser.firefox && evt.altKey && charCode === 0 && keyCode === 0); // Is part of multi-keystroke international character on Safari Mac - var isSafariHalfCharacter = (mybrowser.safari && evt.altKey && keyCode == 229); + var isSafariHalfCharacter = (bowser.safari && evt.altKey && keyCode == 229); if (thisKeyDoesntTriggerNormalize || isFirefoxHalfCharacter || isSafariHalfCharacter) { @@ -4072,7 +4072,7 @@ function Ace2Inner(){ // each of which has node (a magicdom node), index, and maxIndex. If the node // is a text node, maxIndex is the length of the text; else maxIndex is 1. // index is between 0 and maxIndex, inclusive. - if (mybrowser.msie) + if (bowser.msie) { var browserSelection; try @@ -4368,7 +4368,7 @@ function Ace2Inner(){ maxIndex: pt.maxIndex }; } - if (mybrowser.msie) + if (bowser.msie) { // Oddly enough, accessing scrollHeight fixes return key handling on IE 8, // presumably by forcing some kind of internal DOM update. @@ -4663,11 +4663,11 @@ function Ace2Inner(){ if (newHeight < viewHeight) { newHeight = viewHeight; -// if (mybrowser.msie) setIfNecessary(outerWin.document.documentElement.style, 'overflowY', 'auto'); +// if (bowser.msie) setIfNecessary(outerWin.document.documentElement.style, 'overflowY', 'auto'); } else { -// if (mybrowser.msie) setIfNecessary(outerWin.document.documentElement.style, 'overflowY', 'scroll'); +// if (bowser.msie) setIfNecessary(outerWin.document.documentElement.style, 'overflowY', 'scroll'); } if (doesWrap) { @@ -4681,7 +4681,7 @@ function Ace2Inner(){ setIfNecessary(iframe.style, "width", newWidth + "px"); setIfNecessary(sideDiv.style, "height", newHeight + "px"); } - if (mybrowser.firefox) + if (bowser.firefox) { if (!doesWrap) { @@ -4780,14 +4780,14 @@ function Ace2Inner(){ } return false; } - if (mybrowser.msie || mybrowser.safari) + if (bowser.msie || bowser.safari) { setIfNecessary(root, 'contentEditable', (newVal ? 'true' : 'false')); } else { var wasSet = setIfNecessary(doc, 'designMode', (newVal ? 'on' : 'off')); - if (wasSet && newVal && mybrowser.opera) + if (wasSet && newVal && bowser.opera) { // turning on designMode clears event handlers bindTheEventHandlers(); @@ -4853,11 +4853,11 @@ function Ace2Inner(){ $(document).on("keyup", handleKeyEvent); $(document).on("click", handleClick); $(root).on("blur", handleBlur); - if (mybrowser.msie) + if (bowser.msie) { $(document).on("click", handleIEOuterClick); } - if (mybrowser.msie) $(root).on("paste", handleIEPaste); + if (bowser.msie) $(root).on("paste", handleIEPaste); // Don't paste on middle click of links $(root).on("paste", function(e){ @@ -4867,7 +4867,7 @@ function Ace2Inner(){ }) // CompositionEvent is not implemented below IE version 8 - if ( !(mybrowser.msie && mybrowser.version <= 9) && document.documentElement) + if ( !(bowser.msie && bowser.version <= 9) && document.documentElement) { $(document.documentElement).on("compositionstart", handleCompositionEvent); $(document.documentElement).on("compositionend", handleCompositionEvent); @@ -4930,7 +4930,7 @@ function Ace2Inner(){ function handleBlur(evt) { - if (mybrowser.msie) + if (bowser.msie) { // a fix: in IE, clicking on a control like a button outside the // iframe can "blur" the editor, causing it to stop getting @@ -5001,7 +5001,7 @@ function Ace2Inner(){ var win = outerWin; var odoc = win.document; var h; - if (mybrowser.opera) h = win.innerHeight; + if (bowser.opera) h = win.innerHeight; else h = odoc.documentElement.clientHeight; if (h) return h; @@ -5330,9 +5330,9 @@ function Ace2Inner(){ { var body = doc.getElementById("innerdocbody"); root = body; // defined as a var in scope outside - if (mybrowser.firefox) $(root).addClass("mozilla"); - if (mybrowser.safari) $(root).addClass("safari"); - if (mybrowser.msie) $(root).addClass("msie"); + if (bowser.firefox) $(root).addClass("mozilla"); + if (bowser.safari) $(root).addClass("safari"); + if (bowser.msie) $(root).addClass("msie"); setClassPresence(root, "authorColors", true); setClassPresence(root, "doesWrap", doesWrap); From f2891e3b8b3c1b0d34c729ba5c14dc4f291d5b7a Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 21 Jan 2015 14:55:29 +0000 Subject: [PATCH 284/455] working --- src/static/js/ace2_inner.js | 14 +++++++------- src/static/js/pad.js | 1 - 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index dd2f2b124..937398051 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -19,9 +19,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -var _, $, jQuery, plugins, Ace2Common, bowser; -bowser = require('./browser').browser; +var _, $, jQuery, plugins, Ace2Common; +var bowser = require('./browser').browser; if(bowser.msie){ // Honestly fuck IE royally. // Basically every hack we have since V11 causes a problem @@ -30,6 +30,7 @@ if(bowser.msie){ bowser.chrome = true; } } + Ace2Common = require('./ace2_common'); plugins = require('ep_etherpad-lite/static/js/pluginfw/client_plugins'); @@ -43,7 +44,6 @@ var isNodeText = Ace2Common.isNodeText, binarySearchInfinite = Ace2Common.binarySearchInfinite, htmlPrettyEscape = Ace2Common.htmlPrettyEscape, noop = Ace2Common.noop; - var hooks = require('./pluginfw/hooks'); function Ace2Inner(){ @@ -953,7 +953,7 @@ function Ace2Inner(){ showslinenumbers : function(value){ hasLineNumbers = !! value; // disable line numbers on mobile devices - // if (bowser.mobile) hasLineNumbers = false; + if (bowser.mobile) hasLineNumbers = false; setClassPresence(sideDiv, "sidedivhidden", !hasLineNumbers); fixView(); }, @@ -4657,17 +4657,17 @@ function Ace2Inner(){ for (var i = 0; i < 2; i++) { var newHeight = root.clientHeight; - var newWidth = root.clientWidth; + var newWidth = (browser.msie ? root.createTextRange().boundingWidth : root.clientWidth); var viewHeight = getInnerHeight() - iframePadBottom - iframePadTop; var viewWidth = getInnerWidth() - iframePadLeft - iframePadRight; if (newHeight < viewHeight) { newHeight = viewHeight; -// if (bowser.msie) setIfNecessary(outerWin.document.documentElement.style, 'overflowY', 'auto'); + if (bowser.msie) setIfNecessary(outerWin.document.documentElement.style, 'overflowY', 'auto'); } else { -// if (bowser.msie) setIfNecessary(outerWin.document.documentElement.style, 'overflowY', 'scroll'); + if (bowser.msie) setIfNecessary(outerWin.document.documentElement.style, 'overflowY', 'scroll'); } if (doesWrap) { diff --git a/src/static/js/pad.js b/src/static/js/pad.js index 685b420b1..5bbf41236 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.js @@ -494,7 +494,6 @@ var pad = { pad.initTime = +(new Date()); pad.padOptions = clientVars.initialOptions; -console.log("pad", browser); if ((!browser.msie) && (!(browser.firefox && browser.version.indexOf("1.8.") == 0))) { document.domain = document.domain; // for comet From 7237a3b7cc8c43fb206846b6e0492c9d021c7da4 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 21 Jan 2015 14:58:47 +0000 Subject: [PATCH 285/455] bump ueberdb --- src/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/package.json b/src/package.json index 9259b50f6..0598a4b60 100644 --- a/src/package.json +++ b/src/package.json @@ -17,7 +17,7 @@ "etherpad-require-kernel" : "1.0.7", "resolve" : "1.0.0", "socket.io" : "1.3.2", - "ueberDB" : "0.2.10", + "ueberDB" : "0.2.11", "express" : "3.8.1", "async" : "0.9.0", "connect" : "2.7.11", @@ -55,5 +55,5 @@ "repository" : { "type" : "git", "url" : "http://github.com/ether/etherpad-lite.git" }, - "version" : "1.5.0" + "version" : "1.5.1" } From a2cb8a2f1933dff672d5cbe6b7671d83b4f2e948 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 21 Jan 2015 15:01:39 +0000 Subject: [PATCH 286/455] rename back to browser --- src/static/js/ace2_inner.js | 76 ++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 937398051..e68a1b669 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -21,13 +21,13 @@ */ var _, $, jQuery, plugins, Ace2Common; -var bowser = require('./browser').browser; -if(bowser.msie){ +var browser = require('./browser').browser; +if(browser.msie){ // Honestly fuck IE royally. // Basically every hack we have since V11 causes a problem - if(parseInt(bowser.version) >= 11){ - delete bowser.msie; - bowser.chrome = true; + if(parseInt(browser.version) >= 11){ + delete browser.msie; + browser.chrome = true; } } @@ -607,7 +607,7 @@ function Ace2Inner(){ // Chrome can't handle the truth.. If CSS rule white-space:pre-wrap // is true then any paste event will insert two lines.. - if(bowser.chrome){ + if(browser.chrome){ $("#innerdocbody").css({"white-space":"normal"}); } @@ -953,7 +953,7 @@ function Ace2Inner(){ showslinenumbers : function(value){ hasLineNumbers = !! value; // disable line numbers on mobile devices - if (bowser.mobile) hasLineNumbers = false; + if (browser.mobile) hasLineNumbers = false; setClassPresence(sideDiv, "sidedivhidden", !hasLineNumbers); fixView(); }, @@ -1320,7 +1320,7 @@ function Ace2Inner(){ else { var offsetIntoLine = 0; - var filteredFunc = linestylefilter.getFilterStack(text, textAndClassFunc, bowser); + var filteredFunc = linestylefilter.getFilterStack(text, textAndClassFunc, browser); var lineNum = rep.lines.indexOfEntry(lineEntry); var aline = rep.alines[lineNum]; filteredFunc = linestylefilter.getLineStyleFilter( @@ -1568,13 +1568,13 @@ function Ace2Inner(){ lastDirtyNode = (lastDirtyNode && isNodeDirty(lastDirtyNode) && lastDirtyNode); if (firstDirtyNode && lastDirtyNode) { - var cc = makeContentCollector(isStyled, bowser, rep.apool, null, className2Author); + var cc = makeContentCollector(isStyled, browser, rep.apool, null, className2Author); cc.notifySelection(selection); var dirtyNodes = []; for (var n = firstDirtyNode; n && !(n.previousSibling && n.previousSibling == lastDirtyNode); n = n.nextSibling) { - if (bowser.msie) + if (browser.msie) { // try to undo IE's pesky and overzealous linkification try @@ -1614,7 +1614,7 @@ function Ace2Inner(){ if (linesWrapped > 0) { - if(!bowser.msie){ + if(!browser.msie){ // chrome decides in it's infinite wisdom that its okay to put the browsers visisble window in the middle of the span // an outcome of this is that the first chars of the string are no longer visible to the user.. Yay chrome.. // Move the browsers visible area to the left hand side of the span @@ -1919,7 +1919,7 @@ function Ace2Inner(){ if (charsLeft === 0) { var index = 0; - if (bowser.msie && line == (rep.lines.length() - 1) && lineNode.childNodes.length === 0) + if (browser.msie && line == (rep.lines.length() - 1) && lineNode.childNodes.length === 0) { // best to stay at end of last empty div in IE index = 1; @@ -2912,7 +2912,7 @@ function Ace2Inner(){ function doCreateDomLine(nonEmpty) { - if (bowser.msie && (!nonEmpty)) + if (browser.msie && (!nonEmpty)) { var result = { node: null, @@ -2972,7 +2972,7 @@ function Ace2Inner(){ } else { - return domline.createDomLine(nonEmpty, doesWrap, bowser, doc); + return domline.createDomLine(nonEmpty, doesWrap, browser, doc); } } @@ -3239,7 +3239,7 @@ function Ace2Inner(){ var dirtiness = {}; dirtiness.nodeId = uniqueId(n); dirtiness.knownHTML = n.innerHTML; - if (bowser.msie) + if (browser.msie) { // adding a space to an "empty" div in IE designMode doesn't // change the innerHTML of the div's parent; also, other @@ -3256,7 +3256,7 @@ function Ace2Inner(){ var data = getAssoc(n, "dirtiness"); if (!data) return true; if (n.id !== data.nodeId) return true; - if (bowser.msie) + if (browser.msie) { if (n.innerText !== data.knownText) return true; } @@ -3591,7 +3591,7 @@ function Ace2Inner(){ // On Mac and Linux, move right moves to end of word and move left moves to start; // on Windows, always move to start of word. // On Windows, Firefox and IE disagree on whether to stop for punctuation (FF says no). - if (bowser.msie && forwardNotBack) + if (browser.msie && forwardNotBack) { while ((!isDone()) && isWordChar(nextChar())) { @@ -3662,13 +3662,13 @@ function Ace2Inner(){ if (isModKey) return; // If the key is a keypress and the browser is opera and the key is enter, do nothign at all as this fires twice. - if (keyCode == 13 && bowser.opera && (type == "keypress")){ + if (keyCode == 13 && browser.opera && (type == "keypress")){ return; // This stops double enters in Opera but double Tabs still show on single tab keypress, adding keyCode == 9 to this doesn't help as the event is fired twice } var specialHandled = false; - var isTypeForSpecialKey = ((bowser.msie || bowser.safari || bowser.chrome) ? (type == "keydown") : (type == "keypress")); - var isTypeForCmdKey = ((bowser.msie || bowser.safari || bowser.chrome) ? (type == "keydown") : (type == "keypress")); + var isTypeForSpecialKey = ((browser.msie || browser.safari || browser.chrome) ? (type == "keydown") : (type == "keypress")); + var isTypeForCmdKey = ((browser.msie || browser.safari || browser.chrome) ? (type == "keydown") : (type == "keypress")); var stopped = false; inCallStackIfNecessary("handleKeyEvent", function() @@ -3884,7 +3884,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) && bowser.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 @@ -3955,10 +3955,10 @@ function Ace2Inner(){ } // Is part of multi-keystroke international character on Firefox Mac - var isFirefoxHalfCharacter = (bowser.firefox && evt.altKey && charCode === 0 && keyCode === 0); + var isFirefoxHalfCharacter = (browser.firefox && evt.altKey && charCode === 0 && keyCode === 0); // Is part of multi-keystroke international character on Safari Mac - var isSafariHalfCharacter = (bowser.safari && evt.altKey && keyCode == 229); + var isSafariHalfCharacter = (browser.safari && evt.altKey && keyCode == 229); if (thisKeyDoesntTriggerNormalize || isFirefoxHalfCharacter || isSafariHalfCharacter) { @@ -4072,7 +4072,7 @@ function Ace2Inner(){ // each of which has node (a magicdom node), index, and maxIndex. If the node // is a text node, maxIndex is the length of the text; else maxIndex is 1. // index is between 0 and maxIndex, inclusive. - if (bowser.msie) + if (browser.msie) { var browserSelection; try @@ -4368,7 +4368,7 @@ function Ace2Inner(){ maxIndex: pt.maxIndex }; } - if (bowser.msie) + if (browser.msie) { // Oddly enough, accessing scrollHeight fixes return key handling on IE 8, // presumably by forcing some kind of internal DOM update. @@ -4663,11 +4663,11 @@ function Ace2Inner(){ if (newHeight < viewHeight) { newHeight = viewHeight; - if (bowser.msie) setIfNecessary(outerWin.document.documentElement.style, 'overflowY', 'auto'); + if (browser.msie) setIfNecessary(outerWin.document.documentElement.style, 'overflowY', 'auto'); } else { - if (bowser.msie) setIfNecessary(outerWin.document.documentElement.style, 'overflowY', 'scroll'); + if (browser.msie) setIfNecessary(outerWin.document.documentElement.style, 'overflowY', 'scroll'); } if (doesWrap) { @@ -4681,7 +4681,7 @@ function Ace2Inner(){ setIfNecessary(iframe.style, "width", newWidth + "px"); setIfNecessary(sideDiv.style, "height", newHeight + "px"); } - if (bowser.firefox) + if (browser.firefox) { if (!doesWrap) { @@ -4780,14 +4780,14 @@ function Ace2Inner(){ } return false; } - if (bowser.msie || bowser.safari) + if (browser.msie || browser.safari) { setIfNecessary(root, 'contentEditable', (newVal ? 'true' : 'false')); } else { var wasSet = setIfNecessary(doc, 'designMode', (newVal ? 'on' : 'off')); - if (wasSet && newVal && bowser.opera) + if (wasSet && newVal && browser.opera) { // turning on designMode clears event handlers bindTheEventHandlers(); @@ -4853,11 +4853,11 @@ function Ace2Inner(){ $(document).on("keyup", handleKeyEvent); $(document).on("click", handleClick); $(root).on("blur", handleBlur); - if (bowser.msie) + if (browser.msie) { $(document).on("click", handleIEOuterClick); } - if (bowser.msie) $(root).on("paste", handleIEPaste); + if (browser.msie) $(root).on("paste", handleIEPaste); // Don't paste on middle click of links $(root).on("paste", function(e){ @@ -4867,7 +4867,7 @@ function Ace2Inner(){ }) // CompositionEvent is not implemented below IE version 8 - if ( !(bowser.msie && bowser.version <= 9) && document.documentElement) + if ( !(browser.msie && browser.version <= 9) && document.documentElement) { $(document.documentElement).on("compositionstart", handleCompositionEvent); $(document.documentElement).on("compositionend", handleCompositionEvent); @@ -4930,7 +4930,7 @@ function Ace2Inner(){ function handleBlur(evt) { - if (bowser.msie) + if (browser.msie) { // a fix: in IE, clicking on a control like a button outside the // iframe can "blur" the editor, causing it to stop getting @@ -5001,7 +5001,7 @@ function Ace2Inner(){ var win = outerWin; var odoc = win.document; var h; - if (bowser.opera) h = win.innerHeight; + if (browser.opera) h = win.innerHeight; else h = odoc.documentElement.clientHeight; if (h) return h; @@ -5330,9 +5330,9 @@ function Ace2Inner(){ { var body = doc.getElementById("innerdocbody"); root = body; // defined as a var in scope outside - if (bowser.firefox) $(root).addClass("mozilla"); - if (bowser.safari) $(root).addClass("safari"); - if (bowser.msie) $(root).addClass("msie"); + if (browser.firefox) $(root).addClass("mozilla"); + if (browser.safari) $(root).addClass("safari"); + if (browser.msie) $(root).addClass("msie"); setClassPresence(root, "authorColors", true); setClassPresence(root, "doesWrap", doesWrap); From 26a158447074dc3048c26882379cbc09c7c8d4a5 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 21 Jan 2015 15:21:15 +0000 Subject: [PATCH 287/455] fix tests w/ new context --- tests/frontend/specs/bold.js | 2 +- tests/frontend/specs/caret.js | 2 +- tests/frontend/specs/indentation.js | 2 +- tests/frontend/specs/italic.js | 2 +- tests/frontend/specs/redo.js | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/frontend/specs/bold.js b/tests/frontend/specs/bold.js index bffb281b5..b54466e4e 100644 --- a/tests/frontend/specs/bold.js +++ b/tests/frontend/specs/bold.js @@ -44,7 +44,7 @@ describe("bold button", function(){ //select this text element $firstTextElement.sendkeys('{selectall}'); - if(inner$(window)[0].bowser.firefox || inner$(window)[0].bowser.msie){ // if it's a mozilla or IE + if(inner$(window)[0].bowser.firefox || inner$(window)[0].bowser.modernIE){ // if it's a mozilla or IE var evtType = "keypress"; }else{ var evtType = "keydown"; diff --git a/tests/frontend/specs/caret.js b/tests/frontend/specs/caret.js index 6e68b6f52..14ff8d6a6 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.firefox || inner$(window)[0].bowser.msie){ // if it's a mozilla or IE + if(inner$(window)[0].bowser.firefox || inner$(window)[0].bowser.modernIE){ // if it's a mozilla or IE var evtType = "keypress"; }else{ var evtType = "keydown"; diff --git a/tests/frontend/specs/indentation.js b/tests/frontend/specs/indentation.js index c8f2cee81..8e851d873 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.firefox || inner$(window)[0].bowser.msie){ // if it's a mozilla or IE + if(inner$(window)[0].bowser.firefox || inner$(window)[0].bowser.modernIE){ // if it's a mozilla or IE var evtType = "keypress"; }else{ var evtType = "keydown"; diff --git a/tests/frontend/specs/italic.js b/tests/frontend/specs/italic.js index 4ef4b4085..bf7f2bc60 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.firefox || inner$(window)[0].bowser.msie){ // if it's a mozilla or IE + if(inner$(window)[0].bowser.firefox || inner$(window)[0].bowser.modernIE){ // if it's a mozilla or IE var evtType = "keypress"; }else{ var evtType = "keydown"; diff --git a/tests/frontend/specs/redo.js b/tests/frontend/specs/redo.js index 00873d14a..caa32feec 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.firefox || inner$(window)[0].bowser.msie){ // if it's a mozilla or IE + if(inner$(window)[0].bowser.firefox || inner$(window)[0].bowser.modernIE){ // if it's a mozilla or IE var evtType = "keypress"; }else{ var evtType = "keydown"; From 3cd8759cec034534a29b753648263b7d08d6cb75 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 21 Jan 2015 15:21:31 +0000 Subject: [PATCH 288/455] expose modernIE value --- 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 0e122e6a3..4b84e784d 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -28,6 +28,7 @@ if(browser.msie){ if(parseInt(browser.version) >= 11){ delete browser.msie; browser.chrome = true; + browser.modernIE = true; } } From 941cc3c24be22bf5a834e244f78b53836a630bdc Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 21 Jan 2015 15:37:55 +0000 Subject: [PATCH 289/455] url param --- src/static/js/chat.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/static/js/chat.js b/src/static/js/chat.js index 82ca15bdc..2bf233f7a 100644 --- a/src/static/js/chat.js +++ b/src/static/js/chat.js @@ -62,6 +62,7 @@ var chat = (function() $('#users').addClass("chatAndUsers"); $("#chatbox").addClass("chatAndUsersChat"); }else{ + padcookie.setPref("chatAndUsers", false); chat.stickToScreen(false); $('#options-stickychat').prop("disabled", false); $('#users').removeClass("chatAndUsers"); From 17fa87552d97a29a703084efdaa2cb67a1c281b9 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 21 Jan 2015 15:48:21 +0000 Subject: [PATCH 290/455] styling --- src/static/css/pad.css | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/static/css/pad.css b/src/static/css/pad.css index 0ecf91f05..f5ea99d86 100644 --- a/src/static/css/pad.css +++ b/src/static/css/pad.css @@ -918,11 +918,12 @@ input[type=checkbox] { display:block !important; right:0px !important; border-radius:0px !important; - height:200px !important; width:182px !important; margin:2px 0 0 0 !important; border: none !important; border-bottom: 1px solid #ccc !important; + height:155px !important; + border-left: 1px solid #ccc !important; } .chatAndUsersChat > div > #titlecross{ display:none; @@ -932,10 +933,11 @@ input[type=checkbox] { padding:0 !important; margin:0 !important; right:0 !important; - top: 315px !important; + top: 200px !important; width:182px !important; border: none !important; padding:5px !important; + border-left: 1px solid #ccc !important; } @media screen and (max-width: 600px) { From 753e8fdf0b2bde3ef53ee866466ca7be3d215e7b Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 21 Jan 2015 16:08:54 +0000 Subject: [PATCH 291/455] working logic --- src/static/js/chat.js | 7 ++++--- src/static/js/pad.js | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/static/js/chat.js b/src/static/js/chat.js index 2bf233f7a..fb01befec 100644 --- a/src/static/js/chat.js +++ b/src/static/js/chat.js @@ -54,16 +54,17 @@ var chat = (function() isStuck = false; } }, - chatAndUsers: function(status){ - if(status || $('#options-chatandusers').prop('checked')){ + chatAndUsers: function(fromInitialCall) + { + if(fromInitialCall || $('#options-chatandusers').prop('checked')){ padcookie.setPref("chatAndUsers", true); chat.stickToScreen(true); + $('#options-stickychat').prop('checked', true) $('#options-stickychat').prop("disabled", "disabled"); $('#users').addClass("chatAndUsers"); $("#chatbox").addClass("chatAndUsersChat"); }else{ padcookie.setPref("chatAndUsers", false); - chat.stickToScreen(false); $('#options-stickychat').prop("disabled", false); $('#users').removeClass("chatAndUsers"); $("#chatbox").removeClass("chatAndUsersChat"); diff --git a/src/static/js/pad.js b/src/static/js/pad.js index 7c2e22c76..77bfab7f1 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.js @@ -784,6 +784,7 @@ var pad = { handleIsFullyConnected: function(isConnected, isInitialConnect) { pad.determineChatVisibility(isConnected && !isInitialConnect); + pad.determineChatAndUsersVisibility(isConnected && !isInitialConnect); pad.determineAuthorshipColorsVisibility(); }, determineChatVisibility: function(asNowConnectedFeedback){ @@ -796,7 +797,7 @@ var pad = { $('#options-stickychat').prop("checked", false); // set the checkbox for off } }, - determineChatAndUsersVisibility: function(){ + determineChatAndUsersVisibility: function(asNowConnectedFeedback){ var chatAUVisCookie = padcookie.getPref('chatAndUsersVisible'); if(chatAUVisCookie){ // if the cookie is set for chat always visible chat.chatAndUsers(true); // stick it to the screen From aee0fd797dd5c914e07f563ca8f0ea099ecab3bd Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 21 Jan 2015 16:11:00 +0000 Subject: [PATCH 292/455] working translation --- src/locales/en.json | 2 +- src/templates/pad.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/locales/en.json b/src/locales/en.json index de309ba1b..cafbd76e5 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -32,7 +32,7 @@ "pad.settings.padSettings": "Pad Settings", "pad.settings.myView": "My View", "pad.settings.stickychat": "Chat always on screen", - "pad.settings.chatAndUsers": "Show Chat and Users", + "pad.settings.chatandusers": "Show Chat and Users", "pad.settings.colorcheck": "Authorship colors", "pad.settings.linenocheck": "Line numbers", "pad.settings.rtlcheck": "Read content from right to left?", diff --git a/src/templates/pad.html b/src/templates/pad.html index 48b46c410..7c7257cc6 100644 --- a/src/templates/pad.html +++ b/src/templates/pad.html @@ -135,7 +135,7 @@

      - +

      From 657974b5189719ee9b214befdd2fd5f2f3391bed Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 21 Jan 2015 16:40:44 +0000 Subject: [PATCH 293/455] move saved revs notification to gritter --- src/locales/en.json | 1 + src/static/js/pad_savedrevs.js | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/locales/en.json b/src/locales/en.json index cafbd76e5..23bb3a040 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -121,6 +121,7 @@ "timeslider.unnamedauthors": "{{num}} unnamed {[plural(num) one: author, other: authors ]}", "pad.savedrevs.marked": "This revision is now marked as a saved revision", + "pad.savedrevs.timeslider": "You can see saved revisions by visiting the timeslider", "pad.userlist.entername": "Enter your name", "pad.userlist.unnamed": "unnamed", "pad.userlist.guest": "Guest", diff --git a/src/static/js/pad_savedrevs.js b/src/static/js/pad_savedrevs.js index e1552c27c..34323b22f 100644 --- a/src/static/js/pad_savedrevs.js +++ b/src/static/js/pad_savedrevs.js @@ -18,7 +18,16 @@ var pad; exports.saveNow = function(){ pad.collabClient.sendMessage({"type": "SAVE_REVISION"}); - alert(_("pad.savedrevs.marked")); + $.gritter.add({ + // (string | mandatory) the heading of the notification + title: _("pad.savedrevs.marked"), + // (string | mandatory) the text inside the notification + text: _("pad.savedrevs.timeslider") || "You can view saved revisions in the timeslider", + // (bool | optional) if you want it to fade out on its own or just sit there + sticky: false, + // (int | optional) the time you want it to be alive for before fading out + time: '2000' + }); } exports.init = function(_pad){ From 036dea3d0fd06dd549c73ac5a60a6658f66ff127 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 21 Jan 2015 17:25:06 +0000 Subject: [PATCH 294/455] styling --- 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 f5ea99d86..70c15b515 100644 --- a/src/static/css/pad.css +++ b/src/static/css/pad.css @@ -925,6 +925,10 @@ input[type=checkbox] { height:155px !important; border-left: 1px solid #ccc !important; } +.chatAndUsers > #otherusers{ + max-height: 100px; + overflow-y: auto; +} .chatAndUsersChat > div > #titlecross{ display:none; } From 6fd0285d4d48a530677eed08d280901f4fe71b54 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 21 Jan 2015 21:36:11 +0000 Subject: [PATCH 295/455] include lineContent in hok --- src/node/utils/ExportHtml.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/node/utils/ExportHtml.js b/src/node/utils/ExportHtml.js index 5e5aca843..0bb93326b 100644 --- a/src/node/utils/ExportHtml.js +++ b/src/node/utils/ExportHtml.js @@ -30,8 +30,6 @@ function getPadHTML(pad, revNum, callback) var html; async.waterfall([ // fetch revision atext - - function (callback) { if (revNum != undefined) @@ -416,6 +414,7 @@ function getHTMLFromAtext(pad, atext, authorColors) var lineContentFromHook = hooks.callAllStr("getLineHTMLForExport", { line: line, + lineContent: lineContent, apool: apool, attribLine: attribLines[i], text: textLines[i] From 5dce72d419288969cfb82ce105da111aeac25b55 Mon Sep 17 00:00:00 2001 From: John McLear Date: Fri, 23 Jan 2015 01:47:12 +0000 Subject: [PATCH 296/455] chrome list handling fix for #2412 --- src/static/js/contentcollector.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index 1f0620fef..bb20c669d 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -554,7 +554,9 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas } else if ((tname == "div" || tname == "p") && cls && cls.match(/(?:^| )ace-line\b/)) { - oldListTypeOrNull = (_enterList(state, type) || 'none'); + // This has undesirable behavior in Chrome but is right in other browsers. + // See https://github.com/ether/etherpad-lite/issues/2412 for reasoning + if(!abrowser.chrome) oldListTypeOrNull = (_enterList(state, type) || 'none'); } if (className2Author && cls) { From 1f023c52bc8d6ec83174b8387c694b5f313db826 Mon Sep 17 00:00:00 2001 From: Kay Strobach Date: Fri, 23 Jan 2015 08:14:01 +0100 Subject: [PATCH 297/455] Add: How to create an issue How to create an issue and which information should be given --- CONTRIBUTING.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0110151c1..b6e1c2473 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,31 @@ # Developer Guidelines (Please talk to people on the mailing list before you change this page, see our section on [how to get in touch](https://github.com/ether/etherpad-lite#get-in-touch)) +## How to write a bug report + +* Please be polite, we all are humans and problems can occur. +* Please add as much information as possible, for example + * client os(s) and version(s) + * browser(s) and version(s), is the problem reproduceable on different clients + * special environments like firewalls or antivirus + * host os and version + * npm and nodejs version + * Logfiles if available + * steps to reproduce + * what you expected to happen + * what actually happened +* Please format logfiles and code examples with markdown see github Markdown help below the issue textarea for more information. + +If you send logfiles, please set the loglevel switch DEBUG in your settings.json file: + +``` +/* The log level we are using, can be: DEBUG, INFO, WARN, ERROR */ + "loglevel": "DEBUG", +``` + +The logfile location is defined in startup script or the log is directly shown in the commandline after you have started etherpad. + + ## Important note for pull requests **Pull requests should be issued against the develop branch**. We never pull directly into master. From c878a957b7c9696aa228e0bc14fbc1e6cd907b38 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 24 Jan 2015 02:18:59 +0000 Subject: [PATCH 298/455] fix issue in docs --- doc/api/hooks_server-side.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/hooks_server-side.md b/doc/api/hooks_server-side.md index 251cbf11e..32e76ae7f 100644 --- a/doc/api/hooks_server-side.md +++ b/doc/api/hooks_server-side.md @@ -314,7 +314,7 @@ exports.exportHtmlAdditionalTags = function(hook, pad, cb){ var padId = pad.id; cb(["massive","jugs"]); }; - +``` ## userLeave Called from src/node/handler/PadMessageHandler.js From 4ecf0dfad2636a7d4d73dcda1bb51d1ad1a7eec7 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 24 Jan 2015 02:24:10 +0000 Subject: [PATCH 299/455] docs for export HTML --- doc/api/hooks_server-side.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/doc/api/hooks_server-side.md b/doc/api/hooks_server-side.md index 32e76ae7f..c7e7a43af 100644 --- a/doc/api/hooks_server-side.md +++ b/doc/api/hooks_server-side.md @@ -247,6 +247,30 @@ Things in context: This hook will allow a plug-in developer to re-write each line when exporting to HTML. +Example: +``` +var Changeset = require("ep_etherpad-lite/static/js/Changeset"); + +exports.getLineHTMLForExport = function (hook, context) { + var header = _analyzeLine(context.attribLine, context.apool); + if (header) { + return "<" + header + ">" + context.lineContents + ""; + } +} + +function _analyzeLine(alineAttrs, apool) { + var header = null; + if (alineAttrs) { + var opIter = Changeset.opIterator(alineAttrs); + if (opIter.hasNext()) { + var op = opIter.next(); + header = Changeset.opAttributeValue(op, 'heading', apool); + } + } + return header; +} +``` + ## stylesForExport Called from: src/node/utils/ExportHtml.js From 372063295f54990c0bfcb9a29483e92961ae274b Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 24 Jan 2015 04:09:13 +0000 Subject: [PATCH 300/455] beginning of a hook, needs docs etc --- src/node/utils/ExportHtml.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/node/utils/ExportHtml.js b/src/node/utils/ExportHtml.js index 0bb93326b..81fc0be9d 100644 --- a/src/node/utils/ExportHtml.js +++ b/src/node/utils/ExportHtml.js @@ -411,6 +411,18 @@ function getHTMLFromAtext(pad, atext, authorColors) } lists = [] + hooks.aCallAll("asyncLineHTMLForExport", { + line: line, + lineContent: lineContent, + apool: apool, + attribLine: attribLines[i], + text: textLines[i] + }, function(err, newLineContent){ + if(newLineContent.length !== 0) lineContent = newLineContent[0]; + // modified lineContent here + }); + + // Old hook probably not to be used.. var lineContentFromHook = hooks.callAllStr("getLineHTMLForExport", { line: line, @@ -419,6 +431,7 @@ function getHTMLFromAtext(pad, atext, authorColors) attribLine: attribLines[i], text: textLines[i] }, " ", " ", ""); + if (lineContentFromHook) { pieces.push(lineContentFromHook, ''); From 9abb85799c70fb66efbf0828de50869d625a1225 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 24 Jan 2015 04:31:50 +0000 Subject: [PATCH 301/455] realization how aCallAll works --- src/node/utils/ExportHtml.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/node/utils/ExportHtml.js b/src/node/utils/ExportHtml.js index 81fc0be9d..50ff11add 100644 --- a/src/node/utils/ExportHtml.js +++ b/src/node/utils/ExportHtml.js @@ -418,7 +418,12 @@ function getHTMLFromAtext(pad, atext, authorColors) attribLine: attribLines[i], text: textLines[i] }, function(err, newLineContent){ - if(newLineContent.length !== 0) lineContent = newLineContent[0]; +//new Line Content is an array of each of the responses from aCallAll.. We should return a function to it +console.error(newLineContent); + if(newLineContent.length !== 0){ +console.error("lC", newLineContent[0]); + lineContent = newLineContent[0]; + } // modified lineContent here }); From 1890ba39766ab6146683a43c05ae64077272e3d0 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 24 Jan 2015 05:14:38 +0000 Subject: [PATCH 302/455] working, might need polish its pretty late --- src/node/utils/ExportHtml.js | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/src/node/utils/ExportHtml.js b/src/node/utils/ExportHtml.js index 50ff11add..10c2fe5ff 100644 --- a/src/node/utils/ExportHtml.js +++ b/src/node/utils/ExportHtml.js @@ -411,31 +411,26 @@ function getHTMLFromAtext(pad, atext, authorColors) } lists = [] - hooks.aCallAll("asyncLineHTMLForExport", { + var context = { line: line, lineContent: lineContent, apool: apool, attribLine: attribLines[i], text: textLines[i] - }, function(err, newLineContent){ -//new Line Content is an array of each of the responses from aCallAll.. We should return a function to it -console.error(newLineContent); - if(newLineContent.length !== 0){ -console.error("lC", newLineContent[0]); - lineContent = newLineContent[0]; - } - // modified lineContent here + } + + // first context below seems superfluos + hooks.aCallAll("asyncLineHTMLForExport", context, function(err, newLineFunction){ + newLineFunction.forEach(function(fn){ + context.lineContent = fn(context); // note the fn + }); + //new Line Content is an array of each of the responses from aCallAll.. + // We should return a function to it + lineContent = context.lineContent; // modified lineContent here }); // Old hook probably not to be used.. - var lineContentFromHook = hooks.callAllStr("getLineHTMLForExport", - { - line: line, - lineContent: lineContent, - apool: apool, - attribLine: attribLines[i], - text: textLines[i] - }, " ", " ", ""); + var lineContentFromHook = hooks.callAllStr("getLineHTMLForExport", context, " ", " ", ""); if (lineContentFromHook) { From 6b0cf29af6cdf0810910f345357a6ce8409d3f09 Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Sat, 24 Jan 2015 06:34:41 +0100 Subject: [PATCH 303/455] Stop processing a message without auth (i.e. a client did not send CLIENT_READY) --- src/node/handler/PadMessageHandler.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index 3228330e3..7ea5039d8 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -257,11 +257,10 @@ exports.handleMessage = function(client, message) // FIXME: Use a hook instead // FIXME: Allow to override readwrite access with readonly - // FIXME: A message might arrive but wont have an auth object, this is obviously bad so we should deny it // Simulate using the load testing tool if(!sessioninfos[client.id].auth){ console.error("Auth was never applied to a session. If you are using the stress-test tool then restart Etherpad and the Stress test tool.") - callback(); + return; }else{ var auth = sessioninfos[client.id].auth; var checkAccessCallback = function(err, statusObject) From 713b369fc3d3576650e377d9b51abf09f2c1f9f3 Mon Sep 17 00:00:00 2001 From: Stefan Date: Sat, 24 Jan 2015 12:49:17 +0100 Subject: [PATCH 304/455] Add 'last update' column to plugins table --- src/static/js/admin/plugins.js | 4 ++-- src/templates/admin/plugins.html | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/static/js/admin/plugins.js b/src/static/js/admin/plugins.js index a1ad624a4..6c939dc2c 100644 --- a/src/static/js/admin/plugins.js +++ b/src/static/js/admin/plugins.js @@ -141,14 +141,14 @@ $(document).ready(function () { // Sort $('.sort.up').unbind('click').click(function() { - search.sortBy = $(this).text().toLowerCase(); + search.sortBy = $(this).attr('data-label').toLowerCase(); search.sortDir = false; search.offset = 0; search(search.searchTerm, search.results.length); search.results = []; }) $('.sort.down, .sort.none').unbind('click').click(function() { - search.sortBy = $(this).text().toLowerCase(); + search.sortBy = $(this).attr('data-label').toLowerCase(); search.sortDir = true; search.offset = 0; search(search.searchTerm, search.results.length); diff --git a/src/templates/admin/plugins.html b/src/templates/admin/plugins.html index 96ed08fbe..71c4dbcc5 100644 --- a/src/templates/admin/plugins.html +++ b/src/templates/admin/plugins.html @@ -78,6 +78,7 @@ Name Description Version + Last update @@ -86,6 +87,7 @@ +

      From da86110f58400e4b7bc4ce9d0539f97cfcf88d45 Mon Sep 17 00:00:00 2001 From: Stefan Date: Sat, 24 Jan 2015 12:55:17 +0100 Subject: [PATCH 305/455] Use https to download plugins.json --- src/static/js/pluginfw/installer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/static/js/pluginfw/installer.js b/src/static/js/pluginfw/installer.js index 35e42c171..cd2ed3305 100644 --- a/src/static/js/pluginfw/installer.js +++ b/src/static/js/pluginfw/installer.js @@ -61,7 +61,7 @@ exports.availablePlugins = null; var cacheTimestamp = 0; exports.getAvailablePlugins = function(maxCacheAge, cb) { - request("http://static.etherpad.org/plugins.json", function(er, response, plugins){ + request("https://static.etherpad.org/plugins.json", function(er, response, plugins){ if (er) return cb && cb(er); if(exports.availablePlugins && maxCacheAge && Math.round(+new Date/1000)-cacheTimestamp <= maxCacheAge) { return cb && cb(null, exports.availablePlugins) From 378ed022698f044c662f6e1310d161d52c3d7df9 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 24 Jan 2015 13:30:03 +0000 Subject: [PATCH 306/455] docs --- doc/api/hooks_server-side.md | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/doc/api/hooks_server-side.md b/doc/api/hooks_server-side.md index c7e7a43af..75c3902bb 100644 --- a/doc/api/hooks_server-side.md +++ b/doc/api/hooks_server-side.md @@ -245,7 +245,7 @@ Things in context: 2. attribLine - line attributes 3. text - line text -This hook will allow a plug-in developer to re-write each line when exporting to HTML. +This hook will allow a plug-in developer to re-write each line when exporting to HTML. Note that you problably don't want to use this plugin and will probably get better results from `asyncLineHTMLForExport` Example: ``` @@ -271,6 +271,39 @@ function _analyzeLine(alineAttrs, apool) { } ``` +## asyncLineHTMLForExport +Called from: src/node/utils/ExportHtml.js + +Things in context: + +1. The context of the line +2. lineContents - The HTML of the line +3. Attribute pool +4. Attribute line +5. Line Text + +This hook will allow functions to be returned to modify the HTML. + +Example: + +``` +exports.asyncLineHTMLForExport = function (hook, context, cb) { + cb(rewriteLine); +} + +function rewriteLine(context){ + var lineContent = context.lineContent; + sizes.forEach(function(size){ + size = size.replace("fs",""); + if(lineContent){ + lineContent = lineContent.replace(" Date: Sat, 24 Jan 2015 13:39:45 +0000 Subject: [PATCH 307/455] boop --- src/node/utils/ExportHtml.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/node/utils/ExportHtml.js b/src/node/utils/ExportHtml.js index 10c2fe5ff..962f69939 100644 --- a/src/node/utils/ExportHtml.js +++ b/src/node/utils/ExportHtml.js @@ -419,13 +419,14 @@ function getHTMLFromAtext(pad, atext, authorColors) text: textLines[i] } - // first context below seems superfluos + // See https://github.com/ether/etherpad-lite/issues/2486 hooks.aCallAll("asyncLineHTMLForExport", context, function(err, newLineFunction){ + // For each function returned by the hook call + // Process the text based on the function newLineFunction.forEach(function(fn){ context.lineContent = fn(context); // note the fn }); - //new Line Content is an array of each of the responses from aCallAll.. - // We should return a function to it + // We now have a line that has been processed by each hook function lineContent = context.lineContent; // modified lineContent here }); From e41b3ae0a3db1ff7ac8e32d129aa5ebf0ef51fca Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 24 Jan 2015 15:13:26 +0000 Subject: [PATCH 308/455] updated CL --- CHANGELOG.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 762ef39e3..599ff78ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,28 @@ +# 1.5.1 + * NEW: High rez ICON + * NEW: Use HTTPS for plugins.json download + * NEW: Add 'last update' column + * NEW: Show users and chat at the same time + * NEW: Support io.js + * Fix: removeAttributeOnLine now works properly + * Fix: Plugin search and list + * Fix: Issue where unauthed request could cause error + * Fix: Privacy issue with .etherpad export + * Fix: Freeze deps to improve bisectability + * Fix: IE, everything. IE is so broken. + * Fix: Timeslider proxy + * Fix: All backend tests pass + * Fix: Better support for Export into HTML + * Fix: Timeslider stars + * Fix: Translation update + * Fix: Check filesystem if Abiword exists + * Fix: Docs formatting + * Fix: Move Save Revision notification to a gritter message + * Fix: UeberDB MySQL Timeout issue + * Fix: Indented +9 list items + * Fix: Don't paste on middle click of + * SECURITY Fix: Issue where a malformed URL could cause EP to disclose installation location + # 1.5.0 * NEW: Lots of performance improvements for page load times * NEW: Hook for adding CSS to Exports From af7cd91a823cbf13350580ca1ef824694baf9bae Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 24 Jan 2015 15:14:19 +0000 Subject: [PATCH 309/455] formatting --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 599ff78ec..94be6477f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,5 @@ # 1.5.1 - * NEW: High rez ICON + * NEW: High resolution Icon * NEW: Use HTTPS for plugins.json download * NEW: Add 'last update' column * NEW: Show users and chat at the same time From c80a64a379373e72524a313ed5b05d2203672115 Mon Sep 17 00:00:00 2001 From: Stefan Date: Sat, 24 Jan 2015 19:24:20 +0100 Subject: [PATCH 310/455] Update CHANGELOG.md --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 94be6477f..75a9f3276 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ * Fix: Issue where unauthed request could cause error * Fix: Privacy issue with .etherpad export * Fix: Freeze deps to improve bisectability - * Fix: IE, everything. IE is so broken. + * Fix: IE, everything. IE is so broken. * Fix: Timeslider proxy * Fix: All backend tests pass * Fix: Better support for Export into HTML @@ -20,7 +20,7 @@ * Fix: Move Save Revision notification to a gritter message * Fix: UeberDB MySQL Timeout issue * Fix: Indented +9 list items - * Fix: Don't paste on middle click of + * Fix: Don't paste on middle click of link * SECURITY Fix: Issue where a malformed URL could cause EP to disclose installation location # 1.5.0 From f3e5682a0ff99786a9a35745497ad33d5b8d9876 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 24 Jan 2015 18:31:19 +0000 Subject: [PATCH 311/455] fix issue with load of showuserandchat --- src/static/js/chat.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/static/js/chat.js b/src/static/js/chat.js index fb01befec..ce9a00337 100644 --- a/src/static/js/chat.js +++ b/src/static/js/chat.js @@ -22,6 +22,7 @@ var hooks = require('./pluginfw/hooks'); var chat = (function() { var isStuck = false; + var userAndChat = false; var gotInitialMessages = false; var historyPointer = 0; var chatMentions = 0; @@ -56,13 +57,14 @@ var chat = (function() }, chatAndUsers: function(fromInitialCall) { - if(fromInitialCall || $('#options-chatandusers').prop('checked')){ + if(!userAndChat || fromInitialCall){ padcookie.setPref("chatAndUsers", true); chat.stickToScreen(true); $('#options-stickychat').prop('checked', true) $('#options-stickychat').prop("disabled", "disabled"); $('#users').addClass("chatAndUsers"); $("#chatbox").addClass("chatAndUsersChat"); + userAndChat = true; }else{ padcookie.setPref("chatAndUsers", false); $('#options-stickychat').prop("disabled", false); From aca745ddf6a180e17c461bfb352281ee1f3acb1c Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 25 Jan 2015 02:44:10 +0000 Subject: [PATCH 312/455] tell installer if old etherpad needs updating during plugin install --- src/node/hooks/express/adminplugins.js | 2 +- src/static/js/admin/plugins.js | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/node/hooks/express/adminplugins.js b/src/node/hooks/express/adminplugins.js index 8e372f1c0..34eafd0be 100644 --- a/src/node/hooks/express/adminplugins.js +++ b/src/node/hooks/express/adminplugins.js @@ -83,7 +83,7 @@ exports.socketio = function (hook_name, args, cb) { socket.on("install", function (plugin_name) { installer.install(plugin_name, function (er) { if(er) console.warn(er) - socket.emit("finished:install", {plugin: plugin_name, error: er? er.message : null}); + socket.emit("finished:install", {plugin: plugin_name, code: er? er.code : null, error: er? er.message : null}); }); }); diff --git a/src/static/js/admin/plugins.js b/src/static/js/admin/plugins.js index 6c939dc2c..d337da03a 100644 --- a/src/static/js/admin/plugins.js +++ b/src/static/js/admin/plugins.js @@ -225,6 +225,9 @@ $(document).ready(function () { socket.on('finished:install', function(data) { if(data.error) { + if(data.code === "EPEERINVALID"){ + alert("This plugin requires that you update Etherpad so it can operate in it's true glory"); + } alert('An error occured while installing '+data.plugin+' \n'+data.error) $('#installed-plugins .'+data.plugin).remove() } From 4c64b7a670c1db63ad42cfb1e5e78559e0fd1aeb Mon Sep 17 00:00:00 2001 From: Stefan Date: Sun, 25 Jan 2015 22:08:40 +0100 Subject: [PATCH 313/455] Revert 'asyncLineHTMLForExport' hook --- doc/api/hooks_server-side.md | 35 +---------------------------------- src/node/utils/ExportHtml.js | 12 ------------ 2 files changed, 1 insertion(+), 46 deletions(-) diff --git a/doc/api/hooks_server-side.md b/doc/api/hooks_server-side.md index 75c3902bb..c7e7a43af 100644 --- a/doc/api/hooks_server-side.md +++ b/doc/api/hooks_server-side.md @@ -245,7 +245,7 @@ Things in context: 2. attribLine - line attributes 3. text - line text -This hook will allow a plug-in developer to re-write each line when exporting to HTML. Note that you problably don't want to use this plugin and will probably get better results from `asyncLineHTMLForExport` +This hook will allow a plug-in developer to re-write each line when exporting to HTML. Example: ``` @@ -271,39 +271,6 @@ function _analyzeLine(alineAttrs, apool) { } ``` -## asyncLineHTMLForExport -Called from: src/node/utils/ExportHtml.js - -Things in context: - -1. The context of the line -2. lineContents - The HTML of the line -3. Attribute pool -4. Attribute line -5. Line Text - -This hook will allow functions to be returned to modify the HTML. - -Example: - -``` -exports.asyncLineHTMLForExport = function (hook, context, cb) { - cb(rewriteLine); -} - -function rewriteLine(context){ - var lineContent = context.lineContent; - sizes.forEach(function(size){ - size = size.replace("fs",""); - if(lineContent){ - lineContent = lineContent.replace(" Date: Mon, 26 Jan 2015 01:44:40 +0000 Subject: [PATCH 314/455] clean support for image hook --- src/static/js/contentcollector.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index 1f0620fef..ca193f73e 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -458,6 +458,20 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas else { var tname = (dom.nodeTagName(node) || "").toLowerCase(); + + // Images shouldn't be defined as empty. + if (tname == "img"){ + isEmpty = false; + hooks.callAll('collectContentImage', { + cc: cc, + state: state, + tname: tname, + styl: styl, + cls: cls, + node: node + }); + } + if (tname == "br") { this.breakLine = true; From 18121a15071f775d6605719088ea147bb4cd1d14 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 26 Jan 2015 02:32:58 +0000 Subject: [PATCH 315/455] much cleaner --- src/static/js/contentcollector.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index ca193f73e..c0964c4a1 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -461,15 +461,14 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas // Images shouldn't be defined as empty. if (tname == "img"){ - isEmpty = false; - hooks.callAll('collectContentImage', { + var context = hooks.callAll('collectContentImage', { cc: cc, state: state, tname: tname, styl: styl, cls: cls, node: node - }); + }); } if (tname == "br") From c4f1189ebdc92f0da286c3939995f1564d5497dd Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 26 Jan 2015 02:39:43 +0000 Subject: [PATCH 316/455] even cleaner --- src/static/js/contentcollector.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index c0964c4a1..2c8eefbfc 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -459,7 +459,6 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas { var tname = (dom.nodeTagName(node) || "").toLowerCase(); - // Images shouldn't be defined as empty. if (tname == "img"){ var context = hooks.callAll('collectContentImage', { cc: cc, @@ -470,8 +469,7 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas node: node }); } - - if (tname == "br") + else if (tname == "br") { this.breakLine = true; var tvalue = dom.nodeAttr(node, 'value'); From 2c3ce30fedf10e4bbdeeef6bd042a32dae0abd6e Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 26 Jan 2015 02:42:48 +0000 Subject: [PATCH 317/455] docs --- doc/api/hooks_client-side.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/doc/api/hooks_client-side.md b/doc/api/hooks_client-side.md index ca429a075..384085053 100644 --- a/doc/api/hooks_client-side.md +++ b/doc/api/hooks_client-side.md @@ -203,6 +203,28 @@ Things in context: This hook is called before the content of a node is collected by the usual methods. The cc object can be used to do a bunch of things that modify the content of the pad. See, for example, the heading1 plugin for etherpad original. +## collectContentImage +Called from: src/static/js/contentcollector.js + +Things in context: + +1. cc - the contentcollector object +2. state - the current state of the change being made +3. tname - the tag name of this node currently being processed +4. style - the style applied to the node (probably CSS) +5. cls - the HTML class string of the node + +This hook is called before the content of an image node is collected by the usual methods. The cc object can be used to do a bunch of things that modify the content of the pad. + +Example: + +``` +exports.collectContentImage = function(name, context){ + context.state.lineAttributes.img = context.node.outerHTML; +} + +``` + ## collectContentPost Called from: src/static/js/contentcollector.js From b8ac349b530a317675ac7bda5180c02248685f31 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 26 Jan 2015 16:07:46 +0000 Subject: [PATCH 318/455] Update hooks_client-side.md --- doc/api/hooks_client-side.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/api/hooks_client-side.md b/doc/api/hooks_client-side.md index 384085053..8e2d3da79 100644 --- a/doc/api/hooks_client-side.md +++ b/doc/api/hooks_client-side.md @@ -213,6 +213,7 @@ Things in context: 3. tname - the tag name of this node currently being processed 4. style - the style applied to the node (probably CSS) 5. cls - the HTML class string of the node +6. node - the node being modified This hook is called before the content of an image node is collected by the usual methods. The cc object can be used to do a bunch of things that modify the content of the pad. From b6e7ddf8741b5b805fb8393ff08732685ef46b6a Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 26 Jan 2015 16:11:15 +0000 Subject: [PATCH 319/455] bump v --- src/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/package.json b/src/package.json index 0598a4b60..e9923a5af 100644 --- a/src/package.json +++ b/src/package.json @@ -55,5 +55,5 @@ "repository" : { "type" : "git", "url" : "http://github.com/ether/etherpad-lite.git" }, - "version" : "1.5.1" + "version" : "1.5.2" } From 78c3a8d65b1464ac04caeed3f5a8f4e55d989646 Mon Sep 17 00:00:00 2001 From: Siebrand Mazeland Date: Mon, 26 Jan 2015 22:58:30 +0100 Subject: [PATCH 320/455] Localisation updates from https://translatewiki.net. --- src/locales/ar.json | 5 ++++- src/locales/az.json | 2 +- src/locales/br.json | 3 +++ src/locales/hu.json | 2 ++ src/locales/lv.json | 15 ++++++++------- src/locales/oc.json | 11 +++++++---- src/locales/tr.json | 2 ++ src/locales/zh-hans.json | 4 ++-- 8 files changed, 29 insertions(+), 15 deletions(-) diff --git a/src/locales/ar.json b/src/locales/ar.json index b0d19fcc2..9d29b5211 100644 --- a/src/locales/ar.json +++ b/src/locales/ar.json @@ -4,7 +4,8 @@ "Ali1", "Tux-tn", "Alami", - "Meno25" + "Meno25", + "Test Create account" ] }, "index.newPad": "باد جديد", @@ -29,6 +30,7 @@ "pad.colorpicker.save": "تسجيل", "pad.colorpicker.cancel": "إلغاء", "pad.loading": "جاري التحميل...", + "pad.noCookie": "الكوكيز غير متاحة. الرجاء السماح بتحميل الكوكيز على متصفحك!", "pad.passwordRequired": "تحتاج إلى كلمة مرور للوصول إلى هذا الباد", "pad.permissionDenied": "ليس لديك إذن لدخول هذا الباد", "pad.wrongPassword": "كانت كلمة المرور خاطئة", @@ -118,6 +120,7 @@ "pad.impexp.importing": "الاستيراد...", "pad.impexp.confirmimport": "استيراد ملف سيؤدي للكتابة فوق النص الحالي بالباد. هل أنت متأكد من أنك تريد المتابعة؟", "pad.impexp.convertFailed": "لم نتمكن من استيراد هذا الملف. يرجى استخدام تنسيق مستند مختلف، أو النسخ واللصق يدوياً", + "pad.impexp.padHasData": "لا يمكننا استيراد هذا الملف لأن هذه اللوحة تم بالفعل تغييره, الرجاء استيراد لوحة جديد", "pad.impexp.uploadFailed": "فشل التحميل، الرجاء المحاولة مرة أخرى", "pad.impexp.importfailed": "فشل الاستيراد", "pad.impexp.copypaste": "الرجاء نسخ/لصق", diff --git a/src/locales/az.json b/src/locales/az.json index 99d3216aa..f5968ee4c 100644 --- a/src/locales/az.json +++ b/src/locales/az.json @@ -59,7 +59,7 @@ "pad.modals.forcereconnect": "Məcbur təkrarən bağlan", "pad.modals.userdup": "Başqa pəncərədə artıq açıqdır", "pad.modals.userdup.explanation": "Bu lövhə, ola bilsin ki, bu kompüterdəki brauzerin bir neçə pəncərəsində açılmışdır.", - "pad.modals.userdup.advice": "Bu pəncərədən istifadəylə yenidən qoşulun.", + "pad.modals.userdup.advice": "Bu pəncərəni istifadə etmək üçün yenidən qoşul.", "pad.modals.unauth": "İcazəli deyil", "pad.modals.unauth.explanation": "Bu səhifəyə baxdığınız vaxt sizin icazəniz dəyişilib. Bərpa etmək üşün yenidən cəhd edin.", "pad.modals.looping.explanation": "Sinxronlaşdırma serveri ilə kommunikasiya xətası var.", diff --git a/src/locales/br.json b/src/locales/br.json index 6e455d232..6bbd56d2b 100644 --- a/src/locales/br.json +++ b/src/locales/br.json @@ -29,6 +29,7 @@ "pad.colorpicker.save": "Enrollañ", "pad.colorpicker.cancel": "Nullañ", "pad.loading": "O kargañ...", + "pad.noCookie": "N'eus ket gallet kavout an toupin. Aotreit an toupinoù en ho merdeer, mar plij !", "pad.passwordRequired": "Ezhomm ho peus ur ger-tremen evit mont d'ar Pad-se", "pad.permissionDenied": "\nN'oc'h ket aotreet da vont d'ar pad-mañ", "pad.wrongPassword": "Fazius e oa ho ker-tremen", @@ -47,6 +48,7 @@ "pad.importExport.import": "Enkargañ un destenn pe ur restr", "pad.importExport.importSuccessful": "Deuet eo ganeoc'h !", "pad.importExport.export": "Ezporzhiañ ar pad bremañ evel :", + "pad.importExport.exportetherpad": "Etherpad", "pad.importExport.exporthtml": "HTML", "pad.importExport.exportplain": "Testenn blaen", "pad.importExport.exportword": "Microsoft Word", @@ -117,6 +119,7 @@ "pad.impexp.importing": "Oc'h enporzhiañ...", "pad.impexp.confirmimport": "Ma vez enporzhiet ur restr e vo diverket ar pezh zo en teul a-vremañ. Ha sur oc'h e fell deoc'h mont betek penn ?", "pad.impexp.convertFailed": "N'eus ket bet gallet enporzhiañ ar restr. Ober gant ur furmad teul all pe eilañ/pegañ gant an dorn.", + "pad.impexp.padHasData": "N'hon eus ket gallet enporzhiañ ar restr-mañdre ma'z eus bet degaset kemmoù er bloc'h-se ; enporzhiit anezhi war-zu ur bloc'h nevez, mar plij.", "pad.impexp.uploadFailed": "C'hwitet eo bet an enporzhiañ. Klaskit en-dro.", "pad.impexp.importfailed": "C'hwitet eo an enporzhiadenn", "pad.impexp.copypaste": "Eilit/pegit, mar plij", diff --git a/src/locales/hu.json b/src/locales/hu.json index 3102790d7..287e79542 100644 --- a/src/locales/hu.json +++ b/src/locales/hu.json @@ -30,6 +30,7 @@ "pad.colorpicker.save": "Mentés", "pad.colorpicker.cancel": "Mégsem", "pad.loading": "Betöltés…", + "pad.noCookie": "Nem található a süti. Engedélyezd a böngésződben a sütik használatát!", "pad.passwordRequired": "Jelszóra van szükséged ezen notesz eléréséhez", "pad.permissionDenied": "Nincs engedélyed ezen notesz eléréséhez", "pad.wrongPassword": "A jelszó rossz volt", @@ -48,6 +49,7 @@ "pad.importExport.import": "Tetszőleges szövegfájl vagy dokumentum feltöltése", "pad.importExport.importSuccessful": "Siker!", "pad.importExport.export": "Jelenlegi notesz exportálása így:", + "pad.importExport.exportetherpad": "Etherpad", "pad.importExport.exporthtml": "HTML", "pad.importExport.exportplain": "Sima szöveg", "pad.importExport.exportword": "Microsoft Word", diff --git a/src/locales/lv.json b/src/locales/lv.json index acdaaccd1..ee402d331 100644 --- a/src/locales/lv.json +++ b/src/locales/lv.json @@ -9,14 +9,14 @@ "pad.toolbar.bold.title": "Treknrakstā (CTRL + B)", "pad.toolbar.italic.title": "Slīpraksta (Ctrl-es)", "pad.toolbar.underline.title": "Pasvītrojuma (CTRL + U)", - "pad.toolbar.strikethrough.title": "Pārsvītrojums", - "pad.toolbar.ol.title": "Sakārtots saraksts", - "pad.toolbar.ul.title": "Nesakārtots saraksts", - "pad.toolbar.indent.title": "Atkāpe", - "pad.toolbar.unindent.title": "Izkāpe", + "pad.toolbar.strikethrough.title": "Pārsvītrojums (Ctrl+5)", + "pad.toolbar.ol.title": "Sakārtots saraksts (Ctrl+Shift+N)", + "pad.toolbar.ul.title": "Nesakārtots saraksts (Ctrl+Shift+L)", + "pad.toolbar.indent.title": "Atkāpe (TAB)", + "pad.toolbar.unindent.title": "Izkāpe (Shift+TAB)", "pad.toolbar.undo.title": "Atsaukt (CTRL + Z)", "pad.toolbar.redo.title": "Atcelt atsaukšanu (CTRL + Y)", - "pad.toolbar.clearAuthorship.title": "Notīrit autoru krāsas", + "pad.toolbar.clearAuthorship.title": "Notīrit autoru krāsas (Ctrl+Shift+C)", "pad.toolbar.import_export.title": "Importēšanas/eksportēšanas no un uz citu failu formātiem", "pad.toolbar.savedRevision.title": "Saglabāt pārskatīšanu", "pad.toolbar.settings.title": "Iestatījumi", @@ -46,6 +46,7 @@ "pad.importExport.exportword": "Programma Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open dokumenta formāts)", + "pad.modals.connected": "Pievienojies.", "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.", @@ -55,7 +56,7 @@ "pad.modals.deleted": "Dzēsts", "pad.modals.disconnected": "Jūs esat atvienots.", "pad.modals.disconnected.explanation": "Tika zaudēts savienojums ar serveri", - "pad.modals.disconnected.cause": "Iespējams, ka serveris nav pieejams. Lūgums paziņot mums, ja tas turpina notikt.", + "pad.modals.disconnected.cause": "Iespējams, ka serveris nav pieejams. Lūgums paziņot pakalpojuma administratoram, ja tas turpina notikt.", "pad.share": "Koplietot šo pad", "pad.share.readonly": "Tikai lasāms", "pad.share.link": "Saite", diff --git a/src/locales/oc.json b/src/locales/oc.json index 54a375bab..e62d387a5 100644 --- a/src/locales/oc.json +++ b/src/locales/oc.json @@ -9,14 +9,14 @@ "pad.toolbar.bold.title": "Gras (Ctrl-B)", "pad.toolbar.italic.title": "Italica (Ctrl-I)", "pad.toolbar.underline.title": "Soslinhat (Ctrl-U)", - "pad.toolbar.strikethrough.title": "Raiat", - "pad.toolbar.ol.title": "Lista ordenada", - "pad.toolbar.ul.title": "Lista amb de piuses", + "pad.toolbar.strikethrough.title": "Raiat (Ctrl+5)", + "pad.toolbar.ol.title": "Lista ordenada (Ctrl+Shift+N)", + "pad.toolbar.ul.title": "Lista pas ordenada (Ctrl+Shift+L)", "pad.toolbar.indent.title": "Indentar (TAB)", "pad.toolbar.unindent.title": "Desindentar (Maj+TAB)", "pad.toolbar.undo.title": "Anullar (Ctrl-Z)", "pad.toolbar.redo.title": "Restablir (Ctrl-Y)", - "pad.toolbar.clearAuthorship.title": "Escafar las colors qu'identifican los autors", + "pad.toolbar.clearAuthorship.title": "Escafar las colors qu'identifican los autors (Ctrl+Shift+C)", "pad.toolbar.import_export.title": "Importar/Exportar de/cap a un format de fichièr diferent", "pad.toolbar.timeslider.title": "Istoric dinamic", "pad.toolbar.savedRevision.title": "Enregistrar la revision", @@ -26,6 +26,7 @@ "pad.colorpicker.save": "Enregistrar", "pad.colorpicker.cancel": "Anullar", "pad.loading": "Cargament...", + "pad.noCookie": "Lo cookie a pas pogut èsser trobat. Autorizatz los cookies dins vòstre navigador !", "pad.passwordRequired": "Avètz besonh d'un senhal per accedir a aqueste Pad", "pad.permissionDenied": "Vos es pas permés d’accedir a aqueste Pad.", "pad.wrongPassword": "Senhal incorrècte", @@ -44,6 +45,7 @@ "pad.importExport.import": "Cargar un tèxte o un document", "pad.importExport.importSuccessful": "Capitat !", "pad.importExport.export": "Exportar lo Pad actual coma :", + "pad.importExport.exportetherpad": "Etherpad", "pad.importExport.exporthtml": "HTML", "pad.importExport.exportplain": "Tèxte brut", "pad.importExport.exportword": "Microsoft Word", @@ -114,6 +116,7 @@ "pad.impexp.importing": "Impòrt en cors...", "pad.impexp.confirmimport": "Importar un fichièr espotirà lo tèxte actual del blòt. Sètz segur que lo volètz far ?", "pad.impexp.convertFailed": "Podèm pas importar aqueste fichièr. Utilizatz un autre format de document o fasètz un copiar/pegar manual", + "pad.impexp.padHasData": "Avèm pas pogut importar aqueste fichièr perque aqueste blòt a ja agut de modificacions ; importatz cap a un blòt novèl", "pad.impexp.uploadFailed": "Lo telecargament a fracassat, reensajatz", "pad.impexp.importfailed": "Fracàs de l'importacion", "pad.impexp.copypaste": "Copiatz/pegatz", diff --git a/src/locales/tr.json b/src/locales/tr.json index 54030fda1..b0b6dbee3 100644 --- a/src/locales/tr.json +++ b/src/locales/tr.json @@ -30,6 +30,7 @@ "pad.colorpicker.save": "Kaydet", "pad.colorpicker.cancel": "İptal", "pad.loading": "Yükleniyor...", + "pad.noCookie": "Çerez bulunamadı. Lütfen tarayıcınızda çerezlere izin veriniz!", "pad.passwordRequired": "Bu bloknota erişebilmeniz için parolaya ihtiyacınız var", "pad.permissionDenied": "Bu bloknota erişmeye izniniz yok", "pad.wrongPassword": "Parolanız yanlış", @@ -48,6 +49,7 @@ "pad.importExport.import": "Herhangi bir metin dosyası ya da belgesi yükle", "pad.importExport.importSuccessful": "Başarılı!", "pad.importExport.export": "Mevcut bloknotu şu olarak dışa aktar:", + "pad.importExport.exportetherpad": "Etherpad", "pad.importExport.exporthtml": "HTML", "pad.importExport.exportplain": "Düz metin", "pad.importExport.exportword": "Microsoft Word", diff --git a/src/locales/zh-hans.json b/src/locales/zh-hans.json index bc1c97b5a..10727c5d3 100644 --- a/src/locales/zh-hans.json +++ b/src/locales/zh-hans.json @@ -101,8 +101,8 @@ "timeslider.version": "版本 {{version}}", "timeslider.saved": "在{{year}}年{{month}}{{day}}日保存", "timeslider.dateformat": "{{year}}年{{month}}{{day}}日 {{hours}}时:{{minutes}}分:{{seconds}}秒", - "timeslider.month.january": "一月", - "timeslider.month.february": "二月", + "timeslider.month.january": "1月", + "timeslider.month.february": "2月", "timeslider.month.march": "三月", "timeslider.month.april": "四月", "timeslider.month.may": "五月", From 8fbd7d83b637e113f8c86d577fad56bafd4d82c8 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 27 Jan 2015 19:16:36 +0000 Subject: [PATCH 321/455] content collector should also register the blocks, no idea why this wasnt in before --- src/static/js/contentcollector.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index 2c8eefbfc..d34299cc6 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -87,6 +87,10 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas "li": 1 }; + _.each(hooks.callAll('aceRegisterBlockElements'), function(element){ + _blockElems[element] = 1; + }); + function isBlockElement(n) { return !!_blockElems[(dom.nodeTagName(n) || "").toLowerCase()]; From bdfce1cbefa74abb1b0e4d05bf5d5e12bbf5e4f0 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 27 Jan 2015 19:41:01 +0000 Subject: [PATCH 322/455] change to cc.. --- 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 d34299cc6..c85909e94 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -87,7 +87,7 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas "li": 1 }; - _.each(hooks.callAll('aceRegisterBlockElements'), function(element){ + _.each(hooks.callAll('ccRegisterBlockElements'), function(element){ _blockElems[element] = 1; }); From 0216a10a129210abbc36fdbbcd29acf445e75ff5 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 27 Jan 2015 20:10:38 +0000 Subject: [PATCH 323/455] better variable name --- 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 c85909e94..fcb299814 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -464,7 +464,7 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas var tname = (dom.nodeTagName(node) || "").toLowerCase(); if (tname == "img"){ - var context = hooks.callAll('collectContentImage', { + var collectContentImage = hooks.callAll('collectContentImage', { cc: cc, state: state, tname: tname, From b9b110c82fff4d2a6cded4bc674c707f6772b85b Mon Sep 17 00:00:00 2001 From: JulR43 Date: Wed, 28 Jan 2015 17:29:16 +0000 Subject: [PATCH 324/455] Change git-core to git git-core package is an obsolete transitional package, it was renamed git. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0cddb0b0f..48ff434e6 100644 --- a/README.md +++ b/README.md @@ -50,8 +50,8 @@ 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 Debian/Ubuntu*: `apt-get install gzip git curl python libssl-dev pkg-config build-essential` +- *For Fedora/CentOS*: `yum install gzip git 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, we recommend installing/compiling nodejs from source (avoiding apt). From 35da64be5c5438f098ca14acf56ca601b224bb5a Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 28 Jan 2015 19:09:47 +0000 Subject: [PATCH 325/455] hrm bit of a hack, anyone any ideas on better way? --- src/static/js/contentcollector.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index fcb299814..e428c63f9 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -324,7 +324,6 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas return [key, value]; }) ); - lines.appendText('*', Changeset.makeAttribsString('+', attributes , apool)); } cc.startNewLine = function(state) @@ -472,9 +471,13 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas cls: cls, node: node }); + }else{ + // THIS SEEMS VERY HACKY! -- Please submit a better fix! + delete state.lineAttributes.img } - else if (tname == "br") - { + + if (tname == "br") + { this.breakLine = true; var tvalue = dom.nodeAttr(node, 'value'); var induceLineBreak = hooks.callAll('collectContentLineBreak', { @@ -498,7 +501,6 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas { var styl = dom.nodeAttr(node, "style"); var cls = dom.nodeProp(node, "className"); - var isPre = (tname == "pre"); if ((!isPre) && abrowser.safari) { From 9afcd8916782f2a6b53aae061cb7e719764e6ebb Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 4 Feb 2015 17:52:27 +0000 Subject: [PATCH 326/455] bump versions to see if it's stable --- src/package.json | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/package.json b/src/package.json index e9923a5af..17c747653 100644 --- a/src/package.json +++ b/src/package.json @@ -13,10 +13,10 @@ ], "dependencies" : { "etherpad-yajsml" : "0.0.2", - "request" : "2.51.0", + "request" : "2.53.0", "etherpad-require-kernel" : "1.0.7", - "resolve" : "1.0.0", - "socket.io" : "1.3.2", + "resolve" : "1.1.0", + "socket.io" : "1.3.3", "ueberDB" : "0.2.11", "express" : "3.8.1", "async" : "0.9.0", @@ -25,10 +25,9 @@ "uglify-js" : "2.4.16", "formidable" : "1.0.16", "log4js" : "0.6.22", - "nodemailer" : "0.3.44", "cheerio" : "0.18.0", "async-stacktrace" : "0.0.2", - "npm" : "2.2.0", + "npm" : "2.4.1", "ejs" : "1.0.0", "graceful-fs" : "3.0.5", "slide" : "1.1.6", @@ -41,13 +40,13 @@ "swagger-node-express" : "2.1.3", "channels" : "0.0.4", "jsonminify" : "0.2.3", - "measured" : "0.1.6", + "measured" : "1.0.0", "mocha" : "2.1.0", "supertest" : "0.15.0" }, "bin": { "etherpad-lite": "./node/server.js" }, "devDependencies": { - "wd" : "0.0.31" + "wd" : "0.3.11" }, "engines" : { "node" : ">=0.6.3", "npm" : ">=1.0" From b9802616b6cab99a43b470972f62f24fcc21f473 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 4 Feb 2015 17:57:34 +0000 Subject: [PATCH 327/455] use latest 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 7b868307d..da101f8d3 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'); @@ -410,7 +410,8 @@ function compressJS(values) function compressCSS(values) { var complete = values.join("\n"); - return cleanCSS.process(complete); + var minimized = new CleanCSS().minify(complete).styles; + return minimized; } exports.minify = minify; diff --git a/src/package.json b/src/package.json index 17c747653..20ef953d8 100644 --- a/src/package.json +++ b/src/package.json @@ -21,7 +21,7 @@ "express" : "3.8.1", "async" : "0.9.0", "connect" : "2.7.11", - "clean-css" : "0.3.2", + "clean-css" : "3.0.8", "uglify-js" : "2.4.16", "formidable" : "1.0.16", "log4js" : "0.6.22", From 4c667ecef63bbc1029a8ea272f5fc4acc40b3731 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 7 Feb 2015 23:23:33 +0000 Subject: [PATCH 328/455] fix scroll issue where focus is not well managed --- src/static/js/chat.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/static/js/chat.js b/src/static/js/chat.js index ce9a00337..bdb5ee09c 100644 --- a/src/static/js/chat.js +++ b/src/static/js/chat.js @@ -29,9 +29,11 @@ var chat = (function() var self = { show: function () { +console.log("here"); $("#chaticon").hide(); $("#chatbox").show(); $("#gritter-notice-wrapper").hide(); + console.log("scroll down 1"); self.scrollDown(); chatMentions = 0; Tinycon.setBubble(0); @@ -91,7 +93,9 @@ var chat = (function() { if($('#chatbox').css("display") != "none"){ if(!self.lastMessage || !self.lastMessage.position() || self.lastMessage.position().top < $('#chattext').height()) { - $('#chattext').animate({scrollTop: $('#chattext')[0].scrollHeight}, "slow"); + // if we use a slow animate here we can have a race condition when a users focus can not be moved away + // from the last message recieved. + $('#chattext').animate({scrollTop: $('#chattext')[0].scrollHeight}, "fast"); self.lastMessage = $('#chattext > p').eq(-1); } } @@ -195,6 +199,7 @@ var chat = (function() }); if(!isHistoryAdd) self.scrollDown(); + console.log("scroll down 2"); }, init: function(pad) { From fe4dd98a6e4df364523564daeb631898523bfdf4 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 7 Feb 2015 23:24:24 +0000 Subject: [PATCH 329/455] remove console logs --- src/static/js/chat.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/static/js/chat.js b/src/static/js/chat.js index bdb5ee09c..5040ea59b 100644 --- a/src/static/js/chat.js +++ b/src/static/js/chat.js @@ -29,11 +29,9 @@ var chat = (function() var self = { show: function () { -console.log("here"); $("#chaticon").hide(); $("#chatbox").show(); $("#gritter-notice-wrapper").hide(); - console.log("scroll down 1"); self.scrollDown(); chatMentions = 0; Tinycon.setBubble(0); @@ -199,7 +197,6 @@ console.log("here"); }); if(!isHistoryAdd) self.scrollDown(); - console.log("scroll down 2"); }, init: function(pad) { From 1652ed42c508feaa15c4d7f482e57d2ae0d67be7 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 7 Feb 2015 23:23:33 +0000 Subject: [PATCH 330/455] fix scroll issue where focus is not well managed remove console logs --- src/static/js/chat.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/static/js/chat.js b/src/static/js/chat.js index ce9a00337..5040ea59b 100644 --- a/src/static/js/chat.js +++ b/src/static/js/chat.js @@ -91,7 +91,9 @@ var chat = (function() { if($('#chatbox').css("display") != "none"){ if(!self.lastMessage || !self.lastMessage.position() || self.lastMessage.position().top < $('#chattext').height()) { - $('#chattext').animate({scrollTop: $('#chattext')[0].scrollHeight}, "slow"); + // if we use a slow animate here we can have a race condition when a users focus can not be moved away + // from the last message recieved. + $('#chattext').animate({scrollTop: $('#chattext')[0].scrollHeight}, "fast"); self.lastMessage = $('#chattext > p').eq(-1); } } From 7adcd5cba4e6569f34d43280cb740316914d35f8 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 8 Feb 2015 14:34:48 +0000 Subject: [PATCH 331/455] cleaner fix for queue issue --- src/static/js/chat.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/static/js/chat.js b/src/static/js/chat.js index 5040ea59b..9cb5b401d 100644 --- a/src/static/js/chat.js +++ b/src/static/js/chat.js @@ -93,7 +93,7 @@ var chat = (function() if(!self.lastMessage || !self.lastMessage.position() || self.lastMessage.position().top < $('#chattext').height()) { // if we use a slow animate here we can have a race condition when a users focus can not be moved away // from the last message recieved. - $('#chattext').animate({scrollTop: $('#chattext')[0].scrollHeight}, "fast"); + $('#chattext').animate({scrollTop: $('#chattext')[0].scrollHeight}, { duration: 400, queue: false }); self.lastMessage = $('#chattext > p').eq(-1); } } From 4c6bd37286ca2d2639fb0b379108a8a5f4ee4ea0 Mon Sep 17 00:00:00 2001 From: Mike DeRosa Date: Mon, 9 Feb 2015 00:18:12 -0500 Subject: [PATCH 332/455] Adding api call for appending a chat message. --- src/node/db/API.js | 27 ++++++++++++++++++ src/node/handler/APIHandler.js | 47 +++++++++++++++++++++++++++++++ src/node/hooks/express/swagger.js | 4 +++ 3 files changed, 78 insertions(+) diff --git a/src/node/db/API.js b/src/node/db/API.js index 81dedcfef..75e4986bb 100644 --- a/src/node/db/API.js +++ b/src/node/db/API.js @@ -494,6 +494,33 @@ exports.getChatHistory = function(padID, start, end, callback) }); } +/** +appendChatMessage(padID, text, userID, time), creates a chat message for the pad id + +Example returns: + +{code: 0, message:"ok", data: null +{code: 1, message:"padID does not exist", data: null} +*/ +exports.appendChatMessage = function(padID, text, userID, time, callback) +{ + //text is required + if(typeof text != "string") + { + callback(new customError("text is no string","apierror")); + return; + } + + //get the pad + getPadSafe(padID, true, function(err, pad) + { + if(ERR(err, callback)) return; + + pad.appendChatMessage(text, userID, parseInt(time)); + callback(); + }); +} + /*****************/ /**PAD FUNCTIONS */ /*****************/ diff --git a/src/node/handler/APIHandler.js b/src/node/handler/APIHandler.js index a26dd2cfb..5f72b04d9 100644 --- a/src/node/handler/APIHandler.js +++ b/src/node/handler/APIHandler.js @@ -391,6 +391,53 @@ var version = , "getChatHead" : ["padID"] , "restoreRevision" : ["padID", "rev"] } +, "1.2.12": + { "createGroup" : [] + , "createGroupIfNotExistsFor" : ["groupMapper"] + , "deleteGroup" : ["groupID"] + , "listPads" : ["groupID"] + , "listAllPads" : [] + , "createDiffHTML" : ["padID", "startRev", "endRev"] + , "createPad" : ["padID", "text"] + , "createGroupPad" : ["groupID", "padName", "text"] + , "createAuthor" : ["name"] + , "createAuthorIfNotExistsFor": ["authorMapper" , "name"] + , "listPadsOfAuthor" : ["authorID"] + , "createSession" : ["groupID", "authorID", "validUntil"] + , "deleteSession" : ["sessionID"] + , "getSessionInfo" : ["sessionID"] + , "listSessionsOfGroup" : ["groupID"] + , "listSessionsOfAuthor" : ["authorID"] + , "getText" : ["padID", "rev"] + , "setText" : ["padID", "text"] + , "getHTML" : ["padID", "rev"] + , "setHTML" : ["padID", "html"] + , "getAttributePool" : ["padID"] + , "getRevisionsCount" : ["padID"] + , "getRevisionChangeset" : ["padID", "rev"] + , "getLastEdited" : ["padID"] + , "deletePad" : ["padID"] + , "copyPad" : ["sourceID", "destinationID", "force"] + , "movePad" : ["sourceID", "destinationID", "force"] + , "getReadOnlyID" : ["padID"] + , "getPadID" : ["roID"] + , "setPublicStatus" : ["padID", "publicStatus"] + , "getPublicStatus" : ["padID"] + , "setPassword" : ["padID", "password"] + , "isPasswordProtected" : ["padID"] + , "listAuthorsOfPad" : ["padID"] + , "padUsersCount" : ["padID"] + , "getAuthorName" : ["authorID"] + , "padUsers" : ["padID"] + , "sendClientsMessage" : ["padID", "msg"] + , "listAllGroups" : [] + , "checkToken" : [] + , "getChatHistory" : ["padID"] + , "getChatHistory" : ["padID", "start", "end"] + , "getChatHead" : ["padID"] + , "appendChatMessage" : ["padID", "text", "userID", "time"] + , "restoreRevision" : ["padID", "rev"] + } }; // set the latest available API version here diff --git a/src/node/hooks/express/swagger.js b/src/node/hooks/express/swagger.js index b9308dee0..f606eb882 100644 --- a/src/node/hooks/express/swagger.js +++ b/src/node/hooks/express/swagger.js @@ -284,6 +284,10 @@ var API = { } }, "response": {"chatHead":{"type":"Message"}} + }, + "appendChatMessage": { + "func": "appendChatMessage", + "description": "appends a chat message" } } }; From 4af46921e104016cd81cd20f2bff52651797655f Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 9 Feb 2015 17:37:20 +0000 Subject: [PATCH 333/455] better css --- src/static/css/pad.css | 5 +++-- src/static/js/pad_editbar.js | 11 +++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/static/css/pad.css b/src/static/css/pad.css index 70c15b515..e9a91f4e0 100644 --- a/src/static/css/pad.css +++ b/src/static/css/pad.css @@ -919,7 +919,8 @@ input[type=checkbox] { right:0px !important; border-radius:0px !important; width:182px !important; - margin:2px 0 0 0 !important; +/* Below makes UI look weird when X makes editbar flow onto two lines */ +/* margin:2px 0 0 0 !important;*/ border: none !important; border-bottom: 1px solid #ccc !important; height:155px !important; @@ -937,7 +938,7 @@ input[type=checkbox] { padding:0 !important; margin:0 !important; right:0 !important; - top: 200px !important; + top: 200px; width:182px !important; border: none !important; padding:5px !important; diff --git a/src/static/js/pad_editbar.js b/src/static/js/pad_editbar.js index e874614f3..56018b351 100644 --- a/src/static/js/pad_editbar.js +++ b/src/static/js/pad_editbar.js @@ -186,9 +186,20 @@ var padeditbar = (function() $('#editbar').css("height", editbarHeight); $('#editorcontainer').css("top", containerTop); + + // If sticky chat is enabled.. if($('#options-stickychat').is(":checked")){ $('#chatbox').css("top", $('#editorcontainer').offset().top + "px"); }; + + // If chat and Users is enabled.. + if($('#options-chatandusers').is(":checked")){ + $('#users').css("top", $('#editorcontainer').offset().top + "px"); + + // We also need to move the chatbox lower.. + $('#chatbox').css("top", 10 + $('#users').height() + $('#editorcontainer').offset().top + "px"); + } + }, registerDropdownCommand: function (cmd, dropdown) { dropdown = dropdown || cmd; From 8c6507e78c655f402269276f1f1730d431dd18e8 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 9 Feb 2015 18:01:45 +0000 Subject: [PATCH 334/455] more styling stuff --- src/static/css/pad.css | 3 +-- src/static/js/chat.js | 3 ++- src/static/js/pad_editbar.js | 3 --- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/static/css/pad.css b/src/static/css/pad.css index e9a91f4e0..19998418b 100644 --- a/src/static/css/pad.css +++ b/src/static/css/pad.css @@ -936,9 +936,8 @@ input[type=checkbox] { .chatAndUsersChat{ bottom:0px !important; padding:0 !important; - margin:0 !important; + margin: 165px 0px 0px 0px; right:0 !important; - top: 200px; width:182px !important; border: none !important; padding:5px !important; diff --git a/src/static/js/chat.js b/src/static/js/chat.js index 9cb5b401d..102f4637a 100644 --- a/src/static/js/chat.js +++ b/src/static/js/chat.js @@ -57,7 +57,8 @@ var chat = (function() }, chatAndUsers: function(fromInitialCall) { - if(!userAndChat || fromInitialCall){ + var toEnable = $('#options-chatandusers').is(":checked"); + if(toEnable || !userAndChat || fromInitialCall){ padcookie.setPref("chatAndUsers", true); chat.stickToScreen(true); $('#options-stickychat').prop('checked', true) diff --git a/src/static/js/pad_editbar.js b/src/static/js/pad_editbar.js index 56018b351..4fcb20ea6 100644 --- a/src/static/js/pad_editbar.js +++ b/src/static/js/pad_editbar.js @@ -195,9 +195,6 @@ var padeditbar = (function() // If chat and Users is enabled.. if($('#options-chatandusers').is(":checked")){ $('#users').css("top", $('#editorcontainer').offset().top + "px"); - - // We also need to move the chatbox lower.. - $('#chatbox').css("top", 10 + $('#users').height() + $('#editorcontainer').offset().top + "px"); } }, From 5245d2b797856beabd6d7f8d548ac938b867657a Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 9 Feb 2015 18:36:11 +0000 Subject: [PATCH 335/455] and even mroe polish --- src/static/css/pad.css | 2 +- src/static/js/chat.js | 6 +++--- src/static/js/pad_editbar.js | 3 +++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/static/css/pad.css b/src/static/css/pad.css index 19998418b..95ad4f786 100644 --- a/src/static/css/pad.css +++ b/src/static/css/pad.css @@ -897,7 +897,7 @@ input[type=checkbox] { #connectivity, #users { position: absolute; - top: 36px; + top: 38px; right: 20px; display: none; z-index: 500; diff --git a/src/static/js/chat.js b/src/static/js/chat.js index 102f4637a..7fd687eab 100644 --- a/src/static/js/chat.js +++ b/src/static/js/chat.js @@ -22,7 +22,6 @@ var hooks = require('./pluginfw/hooks'); var chat = (function() { var isStuck = false; - var userAndChat = false; var gotInitialMessages = false; var historyPointer = 0; var chatMentions = 0; @@ -58,14 +57,15 @@ var chat = (function() chatAndUsers: function(fromInitialCall) { var toEnable = $('#options-chatandusers').is(":checked"); - if(toEnable || !userAndChat || fromInitialCall){ + if(toEnable || fromInitialCall){ padcookie.setPref("chatAndUsers", true); chat.stickToScreen(true); $('#options-stickychat').prop('checked', true) $('#options-stickychat').prop("disabled", "disabled"); $('#users').addClass("chatAndUsers"); $("#chatbox").addClass("chatAndUsersChat"); - userAndChat = true; + // redraw + padeditbar.redrawHeight() }else{ padcookie.setPref("chatAndUsers", false); $('#options-stickychat').prop("disabled", false); diff --git a/src/static/js/pad_editbar.js b/src/static/js/pad_editbar.js index 4fcb20ea6..4dc80860e 100644 --- a/src/static/js/pad_editbar.js +++ b/src/static/js/pad_editbar.js @@ -187,6 +187,9 @@ var padeditbar = (function() $('#editorcontainer').css("top", containerTop); + // make sure pop ups are in the right place + $('.popup').css("top", $('#editorcontainer').offset().top + "px"); + // If sticky chat is enabled.. if($('#options-stickychat').is(":checked")){ $('#chatbox').css("top", $('#editorcontainer').offset().top + "px"); From b216f9271b07b1171a2be36a6cb72989347a873d Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 9 Feb 2015 19:11:35 +0000 Subject: [PATCH 336/455] should be final logic for chatandusers --- src/static/js/chat.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/static/js/chat.js b/src/static/js/chat.js index 7fd687eab..811b13207 100644 --- a/src/static/js/chat.js +++ b/src/static/js/chat.js @@ -22,6 +22,7 @@ var hooks = require('./pluginfw/hooks'); var chat = (function() { var isStuck = false; + var userAndChat = false; var gotInitialMessages = false; var historyPointer = 0; var chatMentions = 0; @@ -57,14 +58,16 @@ var chat = (function() chatAndUsers: function(fromInitialCall) { var toEnable = $('#options-chatandusers').is(":checked"); - if(toEnable || fromInitialCall){ + if(toEnable || !userAndChat || fromInitialCall){ padcookie.setPref("chatAndUsers", true); chat.stickToScreen(true); $('#options-stickychat').prop('checked', true) + $('#options-chatandusers').prop('checked', true) $('#options-stickychat').prop("disabled", "disabled"); $('#users').addClass("chatAndUsers"); $("#chatbox").addClass("chatAndUsersChat"); // redraw + userAndChat = true; padeditbar.redrawHeight() }else{ padcookie.setPref("chatAndUsers", false); From 26ae37572701a97bec4883d15a98b99971c6bda2 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 9 Feb 2015 19:53:32 +0000 Subject: [PATCH 337/455] minor css polish for user input --- src/static/css/pad.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/static/css/pad.css b/src/static/css/pad.css index 95ad4f786..5e4c4d14e 100644 --- a/src/static/css/pad.css +++ b/src/static/css/pad.css @@ -373,7 +373,7 @@ li[data-key=showusers] > a #online_count { border-radius: 5px; } #myusernameform { - margin-left: 35px + margin-left: 30px } #myusernameedit { font-size: 1.3em; @@ -382,7 +382,7 @@ li[data-key=showusers] > a #online_count { height: 18px; margin: 0; border: 0; - width: 117px; + width: 122px; background: transparent; } #myusernameform input.editable { From 0ad09c17db0ec113c349857067ab3bdaeb70194d Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 9 Feb 2015 23:04:30 +0000 Subject: [PATCH 338/455] fix JS error in timeslider if offset doesn't exist --- src/static/js/pad_editbar.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/static/js/pad_editbar.js b/src/static/js/pad_editbar.js index 4dc80860e..7d0539af9 100644 --- a/src/static/js/pad_editbar.js +++ b/src/static/js/pad_editbar.js @@ -188,16 +188,22 @@ var padeditbar = (function() $('#editorcontainer').css("top", containerTop); // make sure pop ups are in the right place - $('.popup').css("top", $('#editorcontainer').offset().top + "px"); + if($('#editorcontainer').offset()){ + $('.popup').css("top", $('#editorcontainer').offset().top + "px"); + } // If sticky chat is enabled.. if($('#options-stickychat').is(":checked")){ - $('#chatbox').css("top", $('#editorcontainer').offset().top + "px"); + if($('#editorcontainer').offset()){ + $('#chatbox').css("top", $('#editorcontainer').offset().top + "px"); + } }; // If chat and Users is enabled.. if($('#options-chatandusers').is(":checked")){ - $('#users').css("top", $('#editorcontainer').offset().top + "px"); + if($('#editorcontainer').offset()){ + $('#users').css("top", $('#editorcontainer').offset().top + "px"); + } } }, From 275592e423d8adcd6349bbd6bbe5d1940427c65f Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 11 Feb 2015 02:07:20 +0000 Subject: [PATCH 339/455] seems right --- src/static/js/pad_impexp.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/static/js/pad_impexp.js b/src/static/js/pad_impexp.js index 77f1eb289..967615702 100644 --- a/src/static/js/pad_impexp.js +++ b/src/static/js/pad_impexp.js @@ -188,7 +188,8 @@ var padimpexp = (function() pad = _pad; //get /p/padname - var pad_root_path = new RegExp(/.*\/p\/[^\/]+/).exec(document.location.pathname); + // if /p/ isn't available due to a rewrite we use the clientVars padId + var pad_root_path = new RegExp(/.*\/p\/[^\/]+/).exec(document.location.pathname) || clientVars.padId; //get http://example.com/p/padname without Params var pad_root_url = document.location.protocol + '//' + document.location.host + document.location.pathname; From ddc69831b219b7e6bedee910fba805a26b0fb0ca Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 11 Feb 2015 17:59:05 +0000 Subject: [PATCH 340/455] working, need to test though --- src/node/hooks/express.js | 18 +----------------- src/node/hooks/express/adminplugins.js | 4 +++- src/node/utils/Settings.js | 21 +++++++++++++++++++++ src/templates/admin/plugins-info.html | 2 ++ 4 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/node/hooks/express.js b/src/node/hooks/express.js index e858b8008..3275bd3ff 100644 --- a/src/node/hooks/express.js +++ b/src/node/hooks/express.js @@ -10,24 +10,9 @@ var server; var serverName; exports.createServer = function () { - //try to get the git version - var version = ""; - try - { - var rootPath = path.resolve(npm.dir, '..'); - var ref = fs.readFileSync(rootPath + "/.git/HEAD", "utf-8"); - var refPath = rootPath + "/.git/" + ref.substring(5, ref.indexOf("\n")); - version = fs.readFileSync(refPath, "utf-8"); - version = version.substring(0, 7); - console.log("Your Etherpad git version is " + version); - } - catch(e) - { - console.warn("Can't get git version for server header\n" + e.message) - } console.log("Report bugs at https://github.com/ether/etherpad-lite/issues") - serverName = "Etherpad " + version + " (http://etherpad.org)"; + serverName = "Etherpad " + settings.getGitCommit() + " (http://etherpad.org)"; exports.restartServer(); @@ -38,7 +23,6 @@ exports.createServer = function () { else{ console.warn("Admin username and password not set in settings.json. To access admin please uncomment and edit 'users' in settings.json"); } - } exports.restartServer = function () { diff --git a/src/node/hooks/express/adminplugins.js b/src/node/hooks/express/adminplugins.js index 34eafd0be..5015cc5a4 100644 --- a/src/node/hooks/express/adminplugins.js +++ b/src/node/hooks/express/adminplugins.js @@ -1,4 +1,5 @@ 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 plugins = require('ep_etherpad-lite/static/js/pluginfw/plugins'); var _ = require('underscore'); @@ -15,7 +16,8 @@ exports.expressCreateServer = function (hook_name, args, cb) { res.send( eejs.require("ep_etherpad-lite/templates/admin/plugins.html", render_args) ); }); args.app.get('/admin/plugins/info', function(req, res) { - res.send( eejs.require("ep_etherpad-lite/templates/admin/plugins-info.html", {}) ); + var gitCommit = settings.getGitCommit(); + res.send( eejs.require("ep_etherpad-lite/templates/admin/plugins-info.html", {gitCommit:gitCommit}) ); }); } diff --git a/src/node/utils/Settings.js b/src/node/utils/Settings.js index 05ae3bd84..1fb5bddfb 100644 --- a/src/node/utils/Settings.js +++ b/src/node/utils/Settings.js @@ -179,6 +179,25 @@ exports.abiwordAvailable = function() } }; +// Provide git version if available +exports.getGitCommit = function() { + var version = ""; + try + { + var rootPath = path.resolve(npm.dir, '..'); + var ref = fs.readFileSync(rootPath + "/.git/HEAD", "utf-8"); + var refPath = rootPath + "/.git/" + ref.substring(5, ref.indexOf("\n")); + version = fs.readFileSync(refPath, "utf-8"); + version = version.substring(0, 7); + console.log("Your Etherpad git version is " + version); + } + catch(e) + { + console.warn("Can't get git version for server header\n" + e.message) + } + return version; +} + exports.reloadSettings = function reloadSettings() { // Discover where the settings file lives var settingsFilename = argv.settings || "settings.json"; @@ -261,3 +280,5 @@ exports.reloadSettings = function reloadSettings() { // initially load settings exports.reloadSettings(); + + diff --git a/src/templates/admin/plugins-info.html b/src/templates/admin/plugins-info.html index 0ca6d0105..ac1d88bcf 100644 --- a/src/templates/admin/plugins-info.html +++ b/src/templates/admin/plugins-info.html @@ -22,6 +22,8 @@
      +

      Etherpad Git Commit

      +
      <%= gitCommit %>

      Installed plugins

      <%- plugins.formatPlugins().replace(", ","\n") %>
      From d0caebc21f970a0a7d17f4b64b35e48bc0357621 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 11 Feb 2015 18:05:01 +0000 Subject: [PATCH 341/455] may aswell make the git sha a link to the commit --- src/templates/admin/plugins-info.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/templates/admin/plugins-info.html b/src/templates/admin/plugins-info.html index ac1d88bcf..1b328a894 100644 --- a/src/templates/admin/plugins-info.html +++ b/src/templates/admin/plugins-info.html @@ -23,7 +23,7 @@

      Etherpad Git Commit

      -
      <%= gitCommit %>
      +

      <%= gitCommit %>

      Installed plugins

      <%- plugins.formatPlugins().replace(", ","\n") %>
      From e39b4428405d186796c56bb0fd18785bec0b5628 Mon Sep 17 00:00:00 2001 From: John McLear Date: Fri, 13 Feb 2015 01:25:29 +0000 Subject: [PATCH 342/455] bump v for #2505 fix --- src/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/package.json b/src/package.json index 20ef953d8..00876f2cf 100644 --- a/src/package.json +++ b/src/package.json @@ -14,7 +14,7 @@ "dependencies" : { "etherpad-yajsml" : "0.0.2", "request" : "2.53.0", - "etherpad-require-kernel" : "1.0.7", + "etherpad-require-kernel" : "1.0.8", "resolve" : "1.1.0", "socket.io" : "1.3.3", "ueberDB" : "0.2.11", From c191a8716ebee37f61cdd4b07aff41f52e90c604 Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Sat, 14 Feb 2015 16:41:50 +0100 Subject: [PATCH 343/455] totally wrong, introduced by myself in 3354b9406b94e1a04b5eee1c0152914dde73ba89 the first part is always false so the second part which is always true in case a cs deleted some lines was never triggered...sigh --- src/static/js/Changeset.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/static/js/Changeset.js b/src/static/js/Changeset.js index 32da887df..fd658f869 100644 --- a/src/static/js/Changeset.js +++ b/src/static/js/Changeset.js @@ -927,16 +927,12 @@ exports.applyToText = function (cs, str) { break; case '-': removedLines += op.lines; - newlines = strIter.newlines() strIter.skip(op.chars); - if(!(newlines - strIter.newlines() == 0) && (newlines - strIter.newlines() != op.lines)){ - newlinefail = true - } break; case '=': newlines = strIter.newlines() assem.append(strIter.take(op.chars)); - if(!(newlines - strIter.newlines() == op.lines)){ + if(newlines - strIter.newlines() != op.lines){ newlinefail = true } break; From 4313bd27f84d29c604d593a69bd09aee5189b290 Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Sun, 15 Feb 2015 13:25:57 +0100 Subject: [PATCH 344/455] add a comment to make clear that the string in stringIterator does not change; only curIndex is increased. Newlines are counted between curIndex and the end of string. --- src/static/js/Changeset.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/static/js/Changeset.js b/src/static/js/Changeset.js index fd658f869..a7ae8e713 100644 --- a/src/static/js/Changeset.js +++ b/src/static/js/Changeset.js @@ -507,6 +507,7 @@ exports.opAssembler = function () { */ exports.stringIterator = function (str) { var curIndex = 0; + // newLines is the number of \n between curIndex and str.length var newLines = str.split("\n").length - 1 function getnewLines(){ return newLines From b4d4b16b1f86e518be1df24c4fc5e67b923f4a4f Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Sun, 15 Feb 2015 14:56:20 +0100 Subject: [PATCH 345/455] off by 1 --- 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 a7ae8e713..2f1a53bc3 100644 --- a/src/static/js/Changeset.js +++ b/src/static/js/Changeset.js @@ -910,7 +910,7 @@ 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 totalNrOfLines = str.split("\n").length - 1; var removedLines = 0; var unpacked = exports.unpack(cs); exports.assert(str.length == unpacked.oldLen, "mismatched apply: ", str.length, " / ", unpacked.oldLen); From ac2c7e96793f05485627199bc8fd28a4562803cf Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Sun, 15 Feb 2015 15:09:38 +0100 Subject: [PATCH 346/455] add newline counting for - and + op --- src/static/js/Changeset.js | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/static/js/Changeset.js b/src/static/js/Changeset.js index 2f1a53bc3..a91bd1d7e 100644 --- a/src/static/js/Changeset.js +++ b/src/static/js/Changeset.js @@ -924,18 +924,30 @@ exports.applyToText = function (cs, str) { var op = csIter.next(); switch (op.opcode) { case '+': + //op is + and op.lines 0: no newlines must be in op.chars + //op is + and op.lines >0: op.chars must include op.lines newlines + if(op.lines != bankIter.peek(op.chars).split("\n").length - 1){ + newlinefail = true + } assem.append(bankIter.take(op.chars)); break; case '-': + //op is - and op.lines 0: no newlines must be in the deleted string + //op is - and op.lines >0: op.lines newlines must be in the deleted string + if(op.lines != strIter.peek(op.chars).split("\n").length - 1){ + newlinefail = true + } removedLines += op.lines; strIter.skip(op.chars); break; case '=': - newlines = strIter.newlines() - assem.append(strIter.take(op.chars)); - if(newlines - strIter.newlines() != op.lines){ + //op is = and op.lines 0: no newlines must be in the copied string + //op is = and op.lines >0: op.lines newlines must be in the copied string + if(op.lines != strIter.peek(op.chars).split("\n").length - 1){ newlinefail = true } + newlines = strIter.newlines() + assem.append(strIter.take(op.chars)); break; } } From e7f01fa49821020c606ccdd1237d7f6de559e7c0 Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Sun, 15 Feb 2015 15:12:24 +0100 Subject: [PATCH 347/455] remove unnecessary code; because we have checks in every op now we don't need this anymore --- src/static/js/Changeset.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/static/js/Changeset.js b/src/static/js/Changeset.js index a91bd1d7e..f0c7d8b4c 100644 --- a/src/static/js/Changeset.js +++ b/src/static/js/Changeset.js @@ -910,14 +910,11 @@ 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 - 1; - 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); var bankIter = exports.stringIterator(unpacked.charBank); var strIter = exports.stringIterator(str); - var newlines = 0 var newlinefail = false var assem = exports.stringAssembler(); while (csIter.hasNext()) { @@ -937,7 +934,6 @@ exports.applyToText = function (cs, str) { if(op.lines != strIter.peek(op.chars).split("\n").length - 1){ newlinefail = true } - removedLines += op.lines; strIter.skip(op.chars); break; case '=': @@ -946,12 +942,10 @@ exports.applyToText = function (cs, str) { if(op.lines != strIter.peek(op.chars).split("\n").length - 1){ newlinefail = true } - newlines = strIter.newlines() assem.append(strIter.take(op.chars)); break; } } - exports.assert(totalNrOfLines >= removedLines,"cannot remove ", removedLines, " lines from text with ", totalNrOfLines, " lines"); assem.append(strIter.take(strIter.remaining())); return [assem.toString(),newlinefail]; }; From 1f6a9afae4ffbe05064c7a1630df7b0d0750d1e1 Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Sun, 15 Feb 2015 15:23:43 +0100 Subject: [PATCH 348/455] better error message --- 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 f0c7d8b4c..4d0f76a92 100644 --- a/src/static/js/Changeset.js +++ b/src/static/js/Changeset.js @@ -1620,7 +1620,7 @@ exports.makeAText = function (text, attribs) { exports.applyToAText = function (cs, atext, pool) { var text = exports.applyToText(cs, atext.text) if(text[1]){ - throw new Error() + throw new Error("newline count is wrong, cs:",cs," and text:",atext) } return { text: text[0], From c6ef7f4867608706849ce57a211ccc63b52b6f5e Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Sun, 15 Feb 2015 15:26:07 +0100 Subject: [PATCH 349/455] string concat, not multiple arguments... --- 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 4d0f76a92..31b1323c2 100644 --- a/src/static/js/Changeset.js +++ b/src/static/js/Changeset.js @@ -1620,7 +1620,7 @@ exports.makeAText = function (text, attribs) { exports.applyToAText = function (cs, atext, pool) { var text = exports.applyToText(cs, atext.text) if(text[1]){ - throw new Error("newline count is wrong, cs:",cs," and text:",atext) + throw new Error("newline count is wrong, cs:"+cs+" and text:"+atext) } return { text: text[0], From b9dd983f810455639e7b4d17f1327c329af8d9b8 Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Sun, 15 Feb 2015 15:27:27 +0100 Subject: [PATCH 350/455] print the actual text... --- 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 31b1323c2..93a7c5581 100644 --- a/src/static/js/Changeset.js +++ b/src/static/js/Changeset.js @@ -1620,7 +1620,7 @@ exports.makeAText = function (text, attribs) { exports.applyToAText = function (cs, atext, pool) { var text = exports.applyToText(cs, atext.text) if(text[1]){ - throw new Error("newline count is wrong, cs:"+cs+" and text:"+atext) + throw new Error("newline count is wrong, cs:"+cs+" and text:"+atext.text) } return { text: text[0], From 440f74b2c104e050a4ec502403dc68192759f020 Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Sun, 15 Feb 2015 15:47:45 +0100 Subject: [PATCH 351/455] we do not have the text/padid, but at least we can print the changeset --- 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 93a7c5581..6ccfe70f3 100644 --- a/src/static/js/Changeset.js +++ b/src/static/js/Changeset.js @@ -1239,7 +1239,7 @@ exports.mutateAttributionLines = function (cs, lines, pool) { } } - exports.assert(!lineAssem, "line assembler not finished"); + exports.assert(!lineAssem, "line assembler not finished:"+cs); mut.close(); //dmesg("-> "+lines.toSource()); From 66582b19e7a196b77df1e9a912db020189a93014 Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Sun, 15 Feb 2015 16:21:07 +0100 Subject: [PATCH 352/455] 51c14d994756e60333b0b60eccb7255cf0c86461 changed the return value of applyToText to an array that includes if there was an error in the newline part of an changeset op. easysync_tests need to know this too --- src/node/easysync_tests.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/node/easysync_tests.js b/src/node/easysync_tests.js index 374e949fd..93e661b0e 100644 --- a/src/node/easysync_tests.js +++ b/src/node/easysync_tests.js @@ -149,7 +149,7 @@ function runTests() { var correctText = correct.join(''); //print(literal(cs)); - var outText = Changeset.applyToText(cs, inText); + var outText = Changeset.applyToText(cs, inText)[0]; assertEqualStrings(correctText, outText); } @@ -585,9 +585,9 @@ function runTests() { var change123a = Changeset.checkRep(Changeset.compose(change1, change23, p)); assertEqualStrings(change123, change123a); - assertEqualStrings(text2, Changeset.applyToText(change12, startText)); - assertEqualStrings(text3, Changeset.applyToText(change23, text1)); - assertEqualStrings(text3, Changeset.applyToText(change123, startText)); + assertEqualStrings(text2, Changeset.applyToText(change12, startText)[0]); + assertEqualStrings(text3, Changeset.applyToText(change23, text1)[0]); + assertEqualStrings(text3, Changeset.applyToText(change123, startText)[0]); } for (var i = 0; i < 30; i++) testCompose(i); @@ -699,7 +699,7 @@ function runTests() { print("> testMakeSplice"); var t = "a\nb\nc\n"; - var t2 = Changeset.applyToText(Changeset.makeSplice(t, 5, 0, "def"), t); + var t2 = Changeset.applyToText(Changeset.makeSplice(t, 5, 0, "def"), t)[0]; assertEqualStrings("a\nb\ncdef\n", t2); })(); From 0505a4735686322639981f0564757e4543f2706d Mon Sep 17 00:00:00 2001 From: Stefan Date: Sun, 15 Feb 2015 22:21:41 +0100 Subject: [PATCH 353/455] Support node version 0.12.x --- bin/installDeps.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/installDeps.sh b/bin/installDeps.sh index fcf213e48..04c4a02a9 100755 --- a/bin/installDeps.sh +++ b/bin/installDeps.sh @@ -50,9 +50,9 @@ NODE_V_MINOR=$(echo $NODE_VERSION | cut -d "." -f 1-2) if hash iojs 2>/dev/null; then IOJS_VERSION=$(iojs --version) fi -if [ ! $NODE_V_MINOR = "v0.8" ] && [ ! $NODE_V_MINOR = "v0.10" ] && [ ! $NODE_V_MINOR = "v0.11" ]; then +if [ ! $NODE_V_MINOR = "v0.8" ] && [ ! $NODE_V_MINOR = "v0.10" ] && [ ! $NODE_V_MINOR = "v0.11" ] && [ ! $NODE_V_MINOR = "v0.12" ]; then if [ ! $IOJS_VERSION ]; then - echo "You're running a wrong version of node, or io.js is not installed. You're using $NODE_VERSION, we need v0.8.x, v0.10.x or v0.11.x" >&2 + echo "You're running a wrong version of node, or io.js is not installed. You're using $NODE_VERSION, we need v0.8.x, v0.10.x, v0.11.x or v0.12.x" >&2 exit 1 fi fi From 83e1bf0dc4259fed5578174a9dfcf1682acc8854 Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Mon, 16 Feb 2015 03:33:57 +0100 Subject: [PATCH 354/455] add semicolons --- src/static/js/Changeset.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/static/js/Changeset.js b/src/static/js/Changeset.js index 6ccfe70f3..ff4d16c66 100644 --- a/src/static/js/Changeset.js +++ b/src/static/js/Changeset.js @@ -915,7 +915,7 @@ exports.applyToText = function (cs, str) { var csIter = exports.opIterator(unpacked.ops); var bankIter = exports.stringIterator(unpacked.charBank); var strIter = exports.stringIterator(str); - var newlinefail = false + var newlinefail = false; var assem = exports.stringAssembler(); while (csIter.hasNext()) { var op = csIter.next(); @@ -924,7 +924,7 @@ exports.applyToText = function (cs, str) { //op is + and op.lines 0: no newlines must be in op.chars //op is + and op.lines >0: op.chars must include op.lines newlines if(op.lines != bankIter.peek(op.chars).split("\n").length - 1){ - newlinefail = true + newlinefail = true; } assem.append(bankIter.take(op.chars)); break; @@ -932,7 +932,7 @@ exports.applyToText = function (cs, str) { //op is - and op.lines 0: no newlines must be in the deleted string //op is - and op.lines >0: op.lines newlines must be in the deleted string if(op.lines != strIter.peek(op.chars).split("\n").length - 1){ - newlinefail = true + newlinefail = true; } strIter.skip(op.chars); break; @@ -940,7 +940,7 @@ exports.applyToText = function (cs, str) { //op is = and op.lines 0: no newlines must be in the copied string //op is = and op.lines >0: op.lines newlines must be in the copied string if(op.lines != strIter.peek(op.chars).split("\n").length - 1){ - newlinefail = true + newlinefail = true; } assem.append(strIter.take(op.chars)); break; @@ -1620,7 +1620,7 @@ exports.makeAText = function (text, attribs) { exports.applyToAText = function (cs, atext, pool) { var text = exports.applyToText(cs, atext.text) if(text[1]){ - throw new Error("newline count is wrong, cs:"+cs+" and text:"+atext.text) + throw new Error("newline count is wrong, cs:"+cs+" and text:"+atext.text); } return { text: text[0], From 59328aa33a5448d4a3b881de5e33bd93662e2d2c Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Mon, 16 Feb 2015 06:22:49 +0100 Subject: [PATCH 355/455] Revert "51c14d994756e60333b0b60eccb7255cf0c86461 changed the return value of" This reverts commit 66582b19e7a196b77df1e9a912db020189a93014. --- src/node/easysync_tests.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/node/easysync_tests.js b/src/node/easysync_tests.js index 93e661b0e..374e949fd 100644 --- a/src/node/easysync_tests.js +++ b/src/node/easysync_tests.js @@ -149,7 +149,7 @@ function runTests() { var correctText = correct.join(''); //print(literal(cs)); - var outText = Changeset.applyToText(cs, inText)[0]; + var outText = Changeset.applyToText(cs, inText); assertEqualStrings(correctText, outText); } @@ -585,9 +585,9 @@ function runTests() { var change123a = Changeset.checkRep(Changeset.compose(change1, change23, p)); assertEqualStrings(change123, change123a); - assertEqualStrings(text2, Changeset.applyToText(change12, startText)[0]); - assertEqualStrings(text3, Changeset.applyToText(change23, text1)[0]); - assertEqualStrings(text3, Changeset.applyToText(change123, startText)[0]); + assertEqualStrings(text2, Changeset.applyToText(change12, startText)); + assertEqualStrings(text3, Changeset.applyToText(change23, text1)); + assertEqualStrings(text3, Changeset.applyToText(change123, startText)); } for (var i = 0; i < 30; i++) testCompose(i); @@ -699,7 +699,7 @@ function runTests() { print("> testMakeSplice"); var t = "a\nb\nc\n"; - var t2 = Changeset.applyToText(Changeset.makeSplice(t, 5, 0, "def"), t)[0]; + var t2 = Changeset.applyToText(Changeset.makeSplice(t, 5, 0, "def"), t); assertEqualStrings("a\nb\ncdef\n", t2); })(); From c9a5167b41f2a5b247a0a59cd42dd77f29123e71 Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Mon, 16 Feb 2015 06:27:18 +0100 Subject: [PATCH 356/455] throw in applyToText and not in applyToAText --- src/static/js/Changeset.js | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/static/js/Changeset.js b/src/static/js/Changeset.js index ff4d16c66..df180f9cf 100644 --- a/src/static/js/Changeset.js +++ b/src/static/js/Changeset.js @@ -915,7 +915,6 @@ exports.applyToText = function (cs, str) { var csIter = exports.opIterator(unpacked.ops); var bankIter = exports.stringIterator(unpacked.charBank); var strIter = exports.stringIterator(str); - var newlinefail = false; var assem = exports.stringAssembler(); while (csIter.hasNext()) { var op = csIter.next(); @@ -924,7 +923,7 @@ exports.applyToText = function (cs, str) { //op is + and op.lines 0: no newlines must be in op.chars //op is + and op.lines >0: op.chars must include op.lines newlines if(op.lines != bankIter.peek(op.chars).split("\n").length - 1){ - newlinefail = true; + throw new Error("newline count is wrong in op +; cs:"+cs+" and text:"+str); } assem.append(bankIter.take(op.chars)); break; @@ -932,7 +931,7 @@ exports.applyToText = function (cs, str) { //op is - and op.lines 0: no newlines must be in the deleted string //op is - and op.lines >0: op.lines newlines must be in the deleted string if(op.lines != strIter.peek(op.chars).split("\n").length - 1){ - newlinefail = true; + throw new Error("newline count is wrong in op -; cs:"+cs+" and text:"+str); } strIter.skip(op.chars); break; @@ -940,14 +939,14 @@ exports.applyToText = function (cs, str) { //op is = and op.lines 0: no newlines must be in the copied string //op is = and op.lines >0: op.lines newlines must be in the copied string if(op.lines != strIter.peek(op.chars).split("\n").length - 1){ - newlinefail = true; + throw new Error("newline count is wrong in op =; cs:"+cs+" and text:"+str); } assem.append(strIter.take(op.chars)); break; } } assem.append(strIter.take(strIter.remaining())); - return [assem.toString(),newlinefail]; + return assem.toString(); }; /** @@ -1618,12 +1617,8 @@ 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("newline count is wrong, cs:"+cs+" and text:"+atext.text); - } return { - text: text[0], + text: exports.applyToText(cs, atext.text), attribs: exports.applyToAttribution(cs, atext.attribs, pool) }; }; From 37924e441a3c0432088d3c33f544c8c7cefc92eb Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Mon, 16 Feb 2015 09:01:30 +0100 Subject: [PATCH 357/455] add try-catch clause around calls to applyToAText --- src/node/utils/padDiff.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/node/utils/padDiff.js b/src/node/utils/padDiff.js index 88fa5cbad..24d5bb0c2 100644 --- a/src/node/utils/padDiff.js +++ b/src/node/utils/padDiff.js @@ -101,8 +101,12 @@ PadDiff.prototype._createClearStartAtext = function(rev, callback){ return callback(err); } + try { //apply the clearAuthorship changeset var newAText = Changeset.applyToAText(changeset, atext, self._pad.pool); + } catch(err) { + return callback(err) + } callback(null, newAText); }); @@ -209,10 +213,14 @@ PadDiff.prototype._createDiffAtext = function(callback) { if(superChangeset){ var deletionChangeset = self._createDeletionChangeset(superChangeset,atext,self._pad.pool); - //apply the superChangeset, which includes all addings - atext = Changeset.applyToAText(superChangeset,atext,self._pad.pool); - //apply the deletionChangeset, which adds a deletions - atext = Changeset.applyToAText(deletionChangeset,atext,self._pad.pool); + try { + //apply the superChangeset, which includes all addings + atext = Changeset.applyToAText(superChangeset,atext,self._pad.pool); + //apply the deletionChangeset, which adds a deletions + atext = Changeset.applyToAText(deletionChangeset,atext,self._pad.pool); + } catch(err) { + return callback(err) + } } callback(err, atext); From ec6a2b5ba906565124b8cfc4eaffe1a08bb06d18 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 16 Feb 2015 23:02:19 +0000 Subject: [PATCH 358/455] allow for load testing connections to hit by a setting --- settings.json.template | 3 +++ src/node/hooks/express/socketio.js | 8 ++++++-- src/node/utils/Settings.js | 5 +++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/settings.json.template b/settings.json.template index 3f84af9b8..124345e2b 100644 --- a/settings.json.template +++ b/settings.json.template @@ -108,6 +108,9 @@ // restrict socket.io transport methods "socketTransportProtocols" : ["xhr-polling", "jsonp-polling", "htmlfile"], + + // Allow Load Testing tools to hit the Etherpad Instance. Warning this will disable security on the instance. + "loadTest": false, /* The toolbar buttons configuration. "toolbar": { diff --git a/src/node/hooks/express/socketio.js b/src/node/hooks/express/socketio.js index b70aa50ed..35d6d074d 100644 --- a/src/node/hooks/express/socketio.js +++ b/src/node/hooks/express/socketio.js @@ -23,8 +23,12 @@ exports.expressCreateServer = function (hook_name, args, cb) { io.use(function(socket, accept) { var data = socket.request; - if (!data.headers.cookie) return accept('No session cookie transmitted.', false); - + // Use a setting if we want to allow load Testing + if(!data.headers.cookie && settings.loadTest){ + accept(null, true); + }else{ + if (!data.headers.cookie) return accept('No session cookie transmitted.', false); + } // Use connect's cookie parser, because it knows how to parse signed cookies connect.cookieParser(webaccess.secret)(data, {}, function(err){ if(err) { diff --git a/src/node/utils/Settings.js b/src/node/utils/Settings.js index 1fb5bddfb..5382d819a 100644 --- a/src/node/utils/Settings.js +++ b/src/node/utils/Settings.js @@ -144,6 +144,11 @@ exports.loglevel = "INFO"; */ exports.disableIPlogging = false; +/** + * Disable Load Testing + */ +exports.loadTest = false; + /* * log4js appender configuration */ From abb9b6d8330c22f10d55931690004a4fb4d1e6e1 Mon Sep 17 00:00:00 2001 From: Siebrand Mazeland Date: Thu, 19 Feb 2015 13:02:42 +0100 Subject: [PATCH 359/455] Localisation updates from https://translatewiki.net. --- src/locales/awa.json | 69 ++++++++++++++++++++ src/locales/be-tarask.json | 2 + src/locales/cs.json | 10 ++- src/locales/de.json | 4 +- src/locales/el.json | 2 + src/locales/en-gb.json | 127 +++++++++++++++++++++++++++++++++++++ src/locales/es.json | 9 ++- src/locales/eu.json | 4 +- src/locales/fr.json | 2 + src/locales/gl.json | 6 +- src/locales/he.json | 5 ++ src/locales/it.json | 5 +- src/locales/ksh.json | 5 ++ src/locales/mk.json | 2 + src/locales/nl.json | 5 ++ src/locales/pt-br.json | 5 +- src/locales/ru.json | 1 + src/locales/sr-ec.json | 59 +++++++++++++++++ src/locales/sv.json | 2 + src/locales/zh-hans.json | 4 +- 20 files changed, 315 insertions(+), 13 deletions(-) create mode 100644 src/locales/awa.json create mode 100644 src/locales/en-gb.json create mode 100644 src/locales/sr-ec.json diff --git a/src/locales/awa.json b/src/locales/awa.json new file mode 100644 index 000000000..f8c0d501c --- /dev/null +++ b/src/locales/awa.json @@ -0,0 +1,69 @@ +{ + "@metadata": { + "authors": [ + "1AnuraagPandey" + ] + }, + "index.newPad": "नयाँ प्याड", + "pad.toolbar.bold.title": "मोट (Ctrl-B)", + "pad.toolbar.italic.title": "तिरछा (Ctrl+I)", + "pad.toolbar.underline.title": "निम्न रेखाङ्कन (Ctrl-U)", + "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.timeslider.title": "टाइमस्लाइडर", + "pad.toolbar.savedRevision.title": "पुनरावलोकन संग्रह किहा जाय", + "pad.toolbar.settings.title": "सेटिङ्ग", + "pad.colorpicker.save": "सहेजा जाय", + "pad.colorpicker.cancel": "रद्द करा जाय", + "pad.loading": "लोड होत है...", + "pad.wrongPassword": "आप कय पासवर्ड गलत रहा", + "pad.settings.padSettings": "प्याड सेटिङ्ग", + "pad.settings.myView": "हमार दृष्य", + "pad.settings.colorcheck": "लेखकीय रङ्ग", + "pad.settings.linenocheck": "हरफ संख्या", + "pad.settings.fontType": "फन्ट प्रकार:", + "pad.settings.fontType.normal": "साधारण", + "pad.settings.fontType.monospaced": "मोनोस्पेस", + "pad.settings.globalView": "विश्वव्यापी दृष्य", + "pad.settings.language": "भाषा", + "pad.importExport.import_export": "आयात/निर्यात", + "pad.importExport.importSuccessful": "सफल!", + "pad.importExport.exporthtml": "HTML", + "pad.importExport.exportplain": "सामान्य पाठ", + "pad.importExport.exportword": "माइक्रोसफ्ट वर्ड", + "pad.importExport.exportpdf": "पिडिएफ", + "pad.importExport.exportopen": "ओडिएफ(खुल्ला कागजात ढाँचा)", + "pad.modals.unauth": "अनाधिकृत", + "pad.modals.initsocketfail": "सर्भरमा पहुँच से बहरे है ।", + "pad.share.readonly": "पढय वाला खाली", + "pad.share.link": "लिङ्क", + "pad.share.emebdcode": "URL जोडा जाय", + "pad.chat": "बातचीत", + "timeslider.pageTitle": "{{appTitle}} समय रेखा", + "timeslider.toolbar.authors": "लेखक:", + "timeslider.toolbar.exportlink.title": "निर्यात", + "timeslider.version": "संस्करण {{version}}", + "timeslider.dateformat": "{{month}}/{{day}}/{{year}} {{hours}}:{{minutes}}:{{seconds}}", + "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": "डिसेम्बर", + "timeslider.unnamedauthors": "{{num}} unnamed {[plural(num) one: author, other: authors ]}", + "pad.userlist.unnamed": "बेनामी", + "pad.userlist.guest": "पहुना", + "pad.userlist.deny": "अस्वीकार", + "pad.userlist.approve": "स्वीकृत", + "pad.impexp.importing": "आयात होत है...", + "pad.impexp.importfailed": "आयात असफल रहा", + "pad.impexp.copypaste": "कृपया कपी पेस्ट कीन जाय" +} diff --git a/src/locales/be-tarask.json b/src/locales/be-tarask.json index f67d10fe5..3c7898580 100644 --- a/src/locales/be-tarask.json +++ b/src/locales/be-tarask.json @@ -35,6 +35,7 @@ "pad.settings.padSettings": "Налады дакумэнта", "pad.settings.myView": "Мой выгляд", "pad.settings.stickychat": "Заўсёды паказваць чат", + "pad.settings.chatandusers": "Паказаць чат і ўдзельнікаў", "pad.settings.colorcheck": "Колеры аўтарства", "pad.settings.linenocheck": "Нумары радкоў", "pad.settings.rtlcheck": "Тэкст справа-налева", @@ -108,6 +109,7 @@ "timeslider.month.december": "сьнежань", "timeslider.unnamedauthors": "{{num}} {[plural(num) one: безыменны аўтар, few: безыменныя аўтары, many: безыменных аўтараў, other: безыменных аўтараў ]}", "pad.savedrevs.marked": "Гэтая вэрсія цяпер пазначаная як захаваная", + "pad.savedrevs.timeslider": "Вы можаце пабачыць захаваныя вэрсіі з дапамогай шкалы часу", "pad.userlist.entername": "Увядзіце вашае імя", "pad.userlist.unnamed": "безыменны", "pad.userlist.guest": "Госьць", diff --git a/src/locales/cs.json b/src/locales/cs.json index 2a6b5fec1..19552ffd5 100644 --- a/src/locales/cs.json +++ b/src/locales/cs.json @@ -13,11 +13,11 @@ "pad.toolbar.bold.title": "Tučný text (Ctrl-B)", "pad.toolbar.italic.title": "Kurzíva (Ctrl-I)", "pad.toolbar.underline.title": "Podtržené písmo (Ctrl-U)", - "pad.toolbar.strikethrough.title": "Přeskrtnuté písmo", + "pad.toolbar.strikethrough.title": "Přeškrtnuto (Ctrl+5)", "pad.toolbar.ol.title": "Číslovaný seznam", "pad.toolbar.ul.title": "Nečíslovaný seznam", - "pad.toolbar.indent.title": "Odsazení", - "pad.toolbar.unindent.title": "Předsazení", + "pad.toolbar.indent.title": "Odsazení (TAB)", + "pad.toolbar.unindent.title": "Předsazení (Shift+TAB)", "pad.toolbar.undo.title": "Zpět (Ctrl-Z)", "pad.toolbar.redo.title": "Opakovat (Ctrl-Y)", "pad.toolbar.clearAuthorship.title": "Vymazat barvy autorů", @@ -30,12 +30,14 @@ "pad.colorpicker.save": "Uložit", "pad.colorpicker.cancel": "Zrušit", "pad.loading": "Načítání...", + "pad.noCookie": "Nelze nalézt cookie. Povolte prosím cookie ve Vašem prohlížeči.", "pad.passwordRequired": "Pro přístup k tomuto Padu je třeba znát heslo", "pad.permissionDenied": "Nemáte oprávnění pro přístup k tomuto Padu", "pad.wrongPassword": "Nesprávné heslo", "pad.settings.padSettings": "Nastavení Padu", "pad.settings.myView": "Vlastní pohled", "pad.settings.stickychat": "Chat vždy na obrazovce", + "pad.settings.chatandusers": "Ukázat Chat a Uživatele", "pad.settings.colorcheck": "Barvy autorů", "pad.settings.linenocheck": "Čísla řádků", "pad.settings.rtlcheck": "Číst obsah zprava doleva?", @@ -109,6 +111,7 @@ "timeslider.month.december": "prosinec", "timeslider.unnamedauthors": "{{num}} {[ plural(num) one: nejmenovaný Autor, few: nejmenovaní Autoři, other: nejmenovaných Autorů ]}", "pad.savedrevs.marked": "Tato revize je nyní označena jako uložená", + "pad.savedrevs.timeslider": "Návštěvou časové osy zobrazíte uložené revize", "pad.userlist.entername": "Zadejte své jméno", "pad.userlist.unnamed": "nejmenovaný", "pad.userlist.guest": "Host", @@ -119,6 +122,7 @@ "pad.impexp.importing": "Importování…", "pad.impexp.confirmimport": "Import souboru přepíše aktuální text v padu. Opravdu chcete tuto akci provést?", "pad.impexp.convertFailed": "Tento soubor nelze importovat. Použijte prosím jiný formát dokumentu nebo nakopírujte text ručně", + "pad.impexp.padHasData": "Tento soubor jsme nebyly schopni importovat, protože tento Pad již obsahoval změny. Importujte ho prosím do nového padu", "pad.impexp.uploadFailed": "Nahrávání selhalo, zkuste to znovu", "pad.impexp.importfailed": "Import selhal", "pad.impexp.copypaste": "Vložte prosím kopii", diff --git a/src/locales/de.json b/src/locales/de.json index a2bca7237..4888b8e89 100644 --- a/src/locales/de.json +++ b/src/locales/de.json @@ -36,6 +36,7 @@ "pad.settings.padSettings": "Pad Einstellungen", "pad.settings.myView": "Eigene Ansicht", "pad.settings.stickychat": "Chat immer anzeigen", + "pad.settings.chatandusers": "Chat und Benutzer anzeigen", "pad.settings.colorcheck": "Autorenfarben anzeigen", "pad.settings.linenocheck": "Zeilennummern", "pad.settings.rtlcheck": "Inhalt von rechts nach links lesen?", @@ -109,6 +110,7 @@ "timeslider.month.december": "Dezember", "timeslider.unnamedauthors": "{{num}} {[plural(num) one: unbenannter Autor, other: unbenannte Autoren ]}", "pad.savedrevs.marked": "Diese Version wurde jetzt als gespeicherte Version gekennzeichnet", + "pad.savedrevs.timeslider": "Du kannst gespeicherte Versionen durch das Besuchen der Pad-Versionsgeschichte ansehen", "pad.userlist.entername": "Geben Sie Ihren Namen ein", "pad.userlist.unnamed": "unbenannt", "pad.userlist.guest": "Gast", @@ -119,7 +121,7 @@ "pad.impexp.importing": "Importiere …", "pad.impexp.confirmimport": "Das Importieren einer Datei überschreibt den aktuellen Text des Pads. Wollen Sie wirklich fortfahren?", "pad.impexp.convertFailed": "Wir können diese Datei nicht importieren. Bitte verwenden Sie ein anderes Dokumentformat oder übertragen Sie den Text manuell.", - "pad.impexp.padHasData": "Wir konnten diese Datei nicht importieren, da dieses Pad bereits Änderungen hat. Bitte importiere zu einem neuen Pad.", + "pad.impexp.padHasData": "Wir konnten diese Datei nicht importieren, da dieses Pad bereits Änderungen enthält. Bitte importiere sie in ein neues Pad.", "pad.impexp.uploadFailed": "Der Upload ist fehlgeschlagen. Bitte versuchen Sie es erneut.", "pad.impexp.importfailed": "Import fehlgeschlagen", "pad.impexp.copypaste": "Bitte kopieren und einfügen", diff --git a/src/locales/el.json b/src/locales/el.json index 740da95c0..f18c71e47 100644 --- a/src/locales/el.json +++ b/src/locales/el.json @@ -37,6 +37,7 @@ "pad.settings.padSettings": "Ρυθμίσεις Pad", "pad.settings.myView": "Η προβολή μου", "pad.settings.stickychat": "Να είναι πάντα ορατή η συνομιλία", + "pad.settings.chatandusers": "Εμφάνιση Συνομιλίας και Χρηστών", "pad.settings.colorcheck": "Χρώματα συντάκτη", "pad.settings.linenocheck": "Αριθμοί γραμμών", "pad.settings.rtlcheck": "Διαβάζεται το περιεχόμενο από δεξιά προς τα αριστερά;", @@ -110,6 +111,7 @@ "timeslider.month.december": "Δεκεμβρίου", "timeslider.unnamedauthors": "{{num}} {[plural(num) one: ανώνυμος συντάκτης, other: ανώνυμοι συντάκτες]}", "pad.savedrevs.marked": "Αυτή η έκδοση επισημάνθηκε ως αποθηκευμένη έκδοση", + "pad.savedrevs.timeslider": "Μπορείτε να δείτε αποθηκευμένες αναθεωρήσεις στο χρονοδιάγραμμα", "pad.userlist.entername": "Εισάγετε το όνομά σας", "pad.userlist.unnamed": "ανώνυμος", "pad.userlist.guest": "Επισκέπτης", diff --git a/src/locales/en-gb.json b/src/locales/en-gb.json new file mode 100644 index 000000000..258b43316 --- /dev/null +++ b/src/locales/en-gb.json @@ -0,0 +1,127 @@ +{ + "@metadata": { + "authors": [ + "Chase me ladies, I'm the Cavalry", + "Shirayuki" + ] + }, + "index.newPad": "New Pad", + "index.createOpenPad": "or create/open a Pad with the name:", + "pad.toolbar.bold.title": "Bold (Ctrl+B)", + "pad.toolbar.italic.title": "Italic (Ctrl+I)", + "pad.toolbar.underline.title": "Underline (Ctrl+U)", + "pad.toolbar.strikethrough.title": "Strikethrough (Ctrl+5)", + "pad.toolbar.ol.title": "Ordered list (Ctrl+Shift+N)", + "pad.toolbar.ul.title": "Unordered List (Ctrl+Shift+L)", + "pad.toolbar.indent.title": "Indent (Tab)", + "pad.toolbar.unindent.title": "Outdent (Shift+Tab)", + "pad.toolbar.undo.title": "Undo (Ctrl+Z)", + "pad.toolbar.redo.title": "Redo (Ctrl+Y)", + "pad.toolbar.clearAuthorship.title": "Clear Authorship Colours (Ctrl+Shift+C)", + "pad.toolbar.import_export.title": "Import/Export from/to different file formats", + "pad.toolbar.timeslider.title": "Timeslider", + "pad.toolbar.savedRevision.title": "Save Revision", + "pad.toolbar.settings.title": "Settings", + "pad.toolbar.embed.title": "Share and Embed this pad", + "pad.toolbar.showusers.title": "Show the users on this pad", + "pad.colorpicker.save": "Save", + "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", + "pad.settings.padSettings": "Pad Settings", + "pad.settings.myView": "My View", + "pad.settings.stickychat": "Chat always on screen", + "pad.settings.chatandusers": "Show Chat and Users", + "pad.settings.colorcheck": "Authorship colours", + "pad.settings.linenocheck": "Line numbers", + "pad.settings.rtlcheck": "Read content from right to left?", + "pad.settings.fontType": "Font type:", + "pad.settings.fontType.normal": "Normal", + "pad.settings.fontType.monospaced": "Monospace", + "pad.settings.globalView": "Global View", + "pad.settings.language": "Language:", + "pad.importExport.import_export": "Import/Export", + "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", + "pad.importExport.exportpdf": "PDF", + "pad.importExport.exportopen": "ODF (Open Document Format)", + "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.", + "pad.modals.reconnecting": "Reconnecting to your pad..", + "pad.modals.forcereconnect": "Force reconnect", + "pad.modals.userdup": "Opened in another window", + "pad.modals.userdup.explanation": "This pad seems to be opened in more than one browser window on this computer.", + "pad.modals.userdup.advice": "Reconnect to use this window instead.", + "pad.modals.unauth": "Not authorised", + "pad.modals.unauth.explanation": "Your permissions have changed while viewing this page. Try to reconnect.", + "pad.modals.looping.explanation": "There are communication problems with the synchronisation server.", + "pad.modals.looping.cause": "Perhaps you connected through an incompatible firewall or proxy.", + "pad.modals.initsocketfail": "Server is unreachable.", + "pad.modals.initsocketfail.explanation": "Couldn't connect to the synchronisation server.", + "pad.modals.initsocketfail.cause": "This is probably due to a problem with your browser or your internet connection.", + "pad.modals.slowcommit.explanation": "The server is not responding.", + "pad.modals.slowcommit.cause": "This could be due to problems with network connectivity.", + "pad.modals.badChangeset.explanation": "An edit you have made was classified illegal by the synchronisation server.", + "pad.modals.badChangeset.cause": "This could be due to a wrong server configuration or some other unexpected behaviour. Please contact the service administrator, if you feel this is an error. Try to reconnect in order to continue editing.", + "pad.modals.corruptPad.explanation": "The pad you are trying to access is corrupt.", + "pad.modals.corruptPad.cause": "This may be due to a wrong server configuration or some other unexpected behaviour. Please contact the service administrator.", + "pad.modals.deleted": "Deleted.", + "pad.modals.deleted.explanation": "This pad has been removed.", + "pad.modals.disconnected": "You have been disconnected.", + "pad.modals.disconnected.explanation": "The connection to the server was lost", + "pad.modals.disconnected.cause": "The server may be unavailable. Please notify the service administrator if this continues to happen.", + "pad.share": "Share this pad", + "pad.share.readonly": "Read only", + "pad.share.link": "Link", + "pad.share.emebdcode": "Embed URL", + "pad.chat": "Chat", + "pad.chat.title": "Open the chat for this pad.", + "pad.chat.loadmessages": "Load more messages", + "timeslider.pageTitle": "{{appTitle}} Timeslider", + "timeslider.toolbar.returnbutton": "Return to pad", + "timeslider.toolbar.authors": "Authors:", + "timeslider.toolbar.authorsList": "No Authors", + "timeslider.toolbar.exportlink.title": "Export", + "timeslider.exportCurrent": "Export current version as:", + "timeslider.version": "Version {{version}}", + "timeslider.saved": "Saved {{month}} {{day}}, {{year}}", + "timeslider.dateformat": "{{day}}/{{month}}/{{year}} {{hours}}:{{minutes}}:{{seconds}}", + "timeslider.month.january": "January", + "timeslider.month.february": "February", + "timeslider.month.march": "March", + "timeslider.month.april": "April", + "timeslider.month.may": "May", + "timeslider.month.june": "June", + "timeslider.month.july": "July", + "timeslider.month.august": "August", + "timeslider.month.september": "September", + "timeslider.month.october": "October", + "timeslider.month.november": "November", + "timeslider.month.december": "December", + "timeslider.unnamedauthors": "{{num}} unnamed {[plural(num) one: author, other: authors ]}", + "pad.savedrevs.marked": "This revision is now marked as a saved revision", + "pad.savedrevs.timeslider": "You can see saved revisions by visiting the timeslider", + "pad.userlist.entername": "Enter your name", + "pad.userlist.unnamed": "unnamed", + "pad.userlist.guest": "Guest", + "pad.userlist.deny": "Deny", + "pad.userlist.approve": "Approve", + "pad.editbar.clearcolors": "Clear authorship colours on entire document?", + "pad.impexp.importbutton": "Import Now", + "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", + "pad.impexp.exportdisabled": "Exporting as {{type}} format is disabled. Please contact your system administrator for details." +} diff --git a/src/locales/es.json b/src/locales/es.json index 5547d3278..21eb60a78 100644 --- a/src/locales/es.json +++ b/src/locales/es.json @@ -10,7 +10,8 @@ "VegaDark", "Vivaelcelta", "Xuacu", - "Macofe" + "Macofe", + "Fitoschido" ] }, "index.newPad": "Nuevo pad", @@ -42,6 +43,7 @@ "pad.settings.padSettings": "Configuración del pad", "pad.settings.myView": "Preferencias personales", "pad.settings.stickychat": "Chat siempre en pantalla", + "pad.settings.chatandusers": "Mostrar el chat y los usuarios", "pad.settings.colorcheck": "Colores de autoría", "pad.settings.linenocheck": "Números de línea", "pad.settings.rtlcheck": "¿Leer contenido de derecha a izquierda?", @@ -56,11 +58,11 @@ "pad.importExport.export": "Exporta el pad actual como:", "pad.importExport.exportetherpad": "Etherpad", "pad.importExport.exporthtml": "HTML", - "pad.importExport.exportplain": "Texto plano", + "pad.importExport.exportplain": "Texto sin formato", "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.abiword.innerHTML": "Sólo puedes importar formatos de texto plano o html. Para funciones más avanzadas instala abiword.", + "pad.importExport.abiword.innerHTML": "Solo es posible importar texto sin formato o en HTML. Para obtener funciones de importación más avanzadas es necesario instalar AbiWord.", "pad.modals.connected": "Conectado.", "pad.modals.reconnecting": "Reconectando a tu pad..", "pad.modals.forcereconnect": "Forzar reconexión", @@ -115,6 +117,7 @@ "timeslider.month.december": "diciembre", "timeslider.unnamedauthors": "{{num}} {[ plural(num) one: autor desconocido, other: autores desconocidos]}", "pad.savedrevs.marked": "Revisión guardada", + "pad.savedrevs.timeslider": "Puedes ver revisiones guardadas visitando la línea de tiempo", "pad.userlist.entername": "Escribe tu nombre", "pad.userlist.unnamed": "anónimo", "pad.userlist.guest": "Invitado", diff --git a/src/locales/eu.json b/src/locales/eu.json index f12b8d21e..9cd06076c 100644 --- a/src/locales/eu.json +++ b/src/locales/eu.json @@ -1,7 +1,8 @@ { "@metadata": { "authors": [ - "Theklan" + "Theklan", + "Subi" ] }, "index.newPad": "Pad berria", @@ -44,6 +45,7 @@ "pad.importExport.import": "Igo edozein testu fitxategi edo dokumentu", "pad.importExport.importSuccessful": "Arrakastatsua!", "pad.importExport.export": "Oraingo pad hau honela esportatu:", + "pad.importExport.exportetherpad": "Etherpad", "pad.importExport.exporthtml": "HTML", "pad.importExport.exportplain": "Testu laua", "pad.importExport.exportword": "Microsoft Word", diff --git a/src/locales/fr.json b/src/locales/fr.json index 92fcb193a..ba289e3b1 100644 --- a/src/locales/fr.json +++ b/src/locales/fr.json @@ -50,6 +50,7 @@ "pad.settings.padSettings": "Paramètres du pad", "pad.settings.myView": "Ma vue", "pad.settings.stickychat": "Toujours afficher le chat", + "pad.settings.chatandusers": "Afficher la discussion et les utilisateurs", "pad.settings.colorcheck": "Couleurs d’identification", "pad.settings.linenocheck": "Numéros de lignes", "pad.settings.rtlcheck": "Le contenu doit-il être lu de droite à gauche ?", @@ -123,6 +124,7 @@ "timeslider.month.december": "décembre", "timeslider.unnamedauthors": "{{num}} {[plural(num) one: auteur anonyme, other: auteurs anonymes ]}", "pad.savedrevs.marked": "Cette révision est maintenant marquée comme révision enregistrée", + "pad.savedrevs.timeslider": "Vous pouvez voir les révisions enregistrées en visitant l’ascenseur temporel", "pad.userlist.entername": "Entrez votre nom", "pad.userlist.unnamed": "anonyme", "pad.userlist.guest": "Invité", diff --git a/src/locales/gl.json b/src/locales/gl.json index b0ca6532e..381296aaa 100644 --- a/src/locales/gl.json +++ b/src/locales/gl.json @@ -27,13 +27,14 @@ "pad.colorpicker.save": "Gardar", "pad.colorpicker.cancel": "Cancelar", "pad.loading": "Cargando...", - "pad.noCookie": "A cookie non se puido atopar. Por favor, habilite as cookies no seu navegador!", + "pad.noCookie": "Non se puido atopar a cookie. Por favor, habilite as cookies no seu navegador!", "pad.passwordRequired": "Cómpre un contrasinal para acceder a este documento", "pad.permissionDenied": "Non ten permiso para acceder a este documento", "pad.wrongPassword": "O contrasinal era incorrecto", "pad.settings.padSettings": "Configuracións do documento", "pad.settings.myView": "A miña vista", "pad.settings.stickychat": "Chat sempre visible", + "pad.settings.chatandusers": "Mostrar o chat e os usuarios", "pad.settings.colorcheck": "Cores de identificación", "pad.settings.linenocheck": "Números de liña", "pad.settings.rtlcheck": "Quere ler o contido da dereita á esquerda?", @@ -107,6 +108,7 @@ "timeslider.month.december": "decembro", "timeslider.unnamedauthors": "{{num}} {[plural(num) one: autor anónimo, other: autores anónimos ]}", "pad.savedrevs.marked": "Esta revisión está agora marcada como revisión gardada", + "pad.savedrevs.timeslider": "Pode consultar as revisións gardadas visitando a liña do tempo", "pad.userlist.entername": "Insira o seu nome", "pad.userlist.unnamed": "anónimo", "pad.userlist.guest": "Convidado", @@ -117,7 +119,7 @@ "pad.impexp.importing": "Importando...", "pad.impexp.confirmimport": "A importación dun ficheiro ha sobrescribir o texto actual do documento. Está seguro de querer continuar?", "pad.impexp.convertFailed": "Non somos capaces de importar o ficheiro. Utilice un formato de documento diferente ou copie e pegue manualmente", - "pad.impexp.padHasData": "Non puidemos importar este ficheiro porque este Pad xa tivo cambios, por favor, importe a un novo pad.", + "pad.impexp.padHasData": "Non puidemos importar este ficheiro porque este documento xa sufriu cambios; importe a un novo documento.", "pad.impexp.uploadFailed": "Houbo un erro ao cargar o ficheiro; inténteo de novo", "pad.impexp.importfailed": "Fallou a importación", "pad.impexp.copypaste": "Copie e pegue", diff --git a/src/locales/he.json b/src/locales/he.json index 573bc5f61..4222e1533 100644 --- a/src/locales/he.json +++ b/src/locales/he.json @@ -29,12 +29,14 @@ "pad.colorpicker.save": "שמירה", "pad.colorpicker.cancel": "ביטול", "pad.loading": "טעינה...", + "pad.noCookie": "העוגייה לא נמצאה. נא לאפשר עוגיות בדפדפן שלך!", "pad.passwordRequired": "דרושה ססמה כדי לגשת לפנקס הזה", "pad.permissionDenied": "אין לך הרשאה לגשת לפנקס הזה", "pad.wrongPassword": "ססמתך הייתה שגויה", "pad.settings.padSettings": "הגדרות פנקס", "pad.settings.myView": "התצוגה שלי", "pad.settings.stickychat": "השיחה תמיד על המסך", + "pad.settings.chatandusers": "הצגת צ'אט ומשתמשים", "pad.settings.colorcheck": "צביעה לפי מחבר", "pad.settings.linenocheck": "מספרי שורות", "pad.settings.rtlcheck": "לקרוא את התוכן מימין לשמאל?", @@ -47,6 +49,7 @@ "pad.importExport.import": "העלאת כל קובץ טקסט או מסמך", "pad.importExport.importSuccessful": "זה עבד!", "pad.importExport.export": "ייצוא הפנקס הנוכחי בתור:", + "pad.importExport.exportetherpad": "את'רפד", "pad.importExport.exporthtml": "HTML", "pad.importExport.exportplain": "טקסט רגיל", "pad.importExport.exportword": "מיקרוסופט וורד", @@ -107,6 +110,7 @@ "timeslider.month.december": "דצמבר", "timeslider.unnamedauthors": "{[plural(num) one: יוצר אחד, other: {{num}} יוצרים ]} ללא שם", "pad.savedrevs.marked": "גרסה זו מסומנת כגרסה שמורה", + "pad.savedrevs.timeslider": "אפשר להציג גרסאות שמורות באמצעות ביקור בגולל הזמן", "pad.userlist.entername": "נא להזין את שמך", "pad.userlist.unnamed": "ללא שם", "pad.userlist.guest": "אורח", @@ -117,6 +121,7 @@ "pad.impexp.importing": "ייבוא...", "pad.impexp.confirmimport": "ייבוא של קובץ יבטל את הטקסט הנוכחי בפנקס. האם ברצונך להמשיך?", "pad.impexp.convertFailed": "לא הצלחנו לייבא את הקובץ הזה. נא להשתמש בתסדיר מסמך שונה או להעתיק ולהדביק ידנית", + "pad.impexp.padHasData": "לא הצלחנו לייבא את הקובץ הזה, כי בפנקס הזה כבר יש שינויים. נא לייבא לפנקס חדש.", "pad.impexp.uploadFailed": "ההעלאה נכשלה, נא לנסות שוב", "pad.impexp.importfailed": "הייבוא נכשל", "pad.impexp.copypaste": "נא להעתיק ולהדביק", diff --git a/src/locales/it.json b/src/locales/it.json index 02ae8a9f7..a6c30d963 100644 --- a/src/locales/it.json +++ b/src/locales/it.json @@ -5,7 +5,8 @@ "Gianfranco", "Muxator", "Vituzzu", - "Macofe" + "Macofe", + "Nivit" ] }, "index.newPad": "Nuovo Pad", @@ -36,6 +37,7 @@ "pad.settings.padSettings": "Impostazioni del Pad", "pad.settings.myView": "Mia visualizzazione", "pad.settings.stickychat": "Chat sempre sullo schermo", + "pad.settings.chatandusers": "Mostra chat e utenti", "pad.settings.colorcheck": "Colori che indicano gli autori", "pad.settings.linenocheck": "Numeri di riga", "pad.settings.rtlcheck": "Leggere il contenuto da destra a sinistra?", @@ -48,6 +50,7 @@ "pad.importExport.import": "Carica un file di testo o un documento", "pad.importExport.importSuccessful": "Riuscito!", "pad.importExport.export": "Esportare il Pad corrente come:", + "pad.importExport.exportetherpad": "Etherpad", "pad.importExport.exporthtml": "HTML", "pad.importExport.exportplain": "Solo testo", "pad.importExport.exportword": "Microsoft Word", diff --git a/src/locales/ksh.json b/src/locales/ksh.json index 8443e0ec9..f851bbf1d 100644 --- a/src/locales/ksh.json +++ b/src/locales/ksh.json @@ -26,12 +26,14 @@ "pad.colorpicker.save": "Faßhallde", "pad.colorpicker.cancel": "Ophüüre", "pad.loading": "Ben aam Laade …", + "pad.noCookie": "Dat Pläzje wood nit jevonge. Don dat en Dingem Brauser zohlohße!", "pad.passwordRequired": "Do bruchs e Paßwoot för heh dat Pädd.", "pad.permissionDenied": "Do häs nit dat Rääsch, op heh dat Pädd zohzejriife.", "pad.wrongPassword": "Ding Paßwoot wohr verkeht.", "pad.settings.padSettings": "Däm Pädd sing Enschtällonge", "pad.settings.myView": "Aanseesch", "pad.settings.stickychat": "Donn der Klaaf emmer aanzeije", + "pad.settings.chatandusers": "Dunn de Metmaacher un der Klaaf aanzeije", "pad.settings.colorcheck": "Färve för de Schriiver", "pad.settings.linenocheck": "Nommere för de Reije", "pad.settings.rtlcheck": "Schreff vun Rääschß noh Lenks?", @@ -44,6 +46,7 @@ "pad.importExport.import": "Donn jeede Täx udder jeede Zoot Dokemänt huhlaade", "pad.importExport.importSuccessful": "Jeschaff!", "pad.importExport.export": "Don dat Pädd äxpoteere alß:", + "pad.importExport.exportetherpad": "Etherpad", "pad.importExport.exporthtml": "HTML", "pad.importExport.exportplain": "Eijfach Täx", "pad.importExport.exportword": "Microsoft Word", @@ -104,6 +107,7 @@ "timeslider.month.december": "Dezämber", "timeslider.unnamedauthors": "{[plural(num) zero: keine, one: eine, other: {{num}} ]} nahmeloose Schriiver", "pad.savedrevs.marked": "Heh di Väsjohn es jäz faßjehallde.", + "pad.savedrevs.timeslider": "Mer kann de faßjehallde Väsjohne belohre beim Verjangeheid afschpelle", "pad.userlist.entername": "Jif Dinge Nahme en", "pad.userlist.unnamed": "nahmeloßß", "pad.userlist.guest": "Jaßß", @@ -114,6 +118,7 @@ "pad.impexp.importing": "Ben aam Empotteere …", "pad.impexp.confirmimport": "En Dattei ze empotteere määt der janze Täx em Pädd fott. Wells De dat verfaftesch hann?", "pad.impexp.convertFailed": "Mer kunnte di Dattei nit empoteere. Nemm en ander Dattei-Fommaat udder donn dä Täx vun Hand kopeere un ennföhje.", + "pad.impexp.padHasData": "Mer kunnte di Dattei nit empottehre weil et Pädd alt Veränderonge metjemaht hät. Donn se en e neu Pädd empottehre.", "pad.impexp.uploadFailed": "Dat Huhlaade es donävve jejange. Bes esu johd un probeer et norr_ens.", "pad.impexp.importfailed": "Et Empoteere es donävve jejange.", "pad.impexp.copypaste": "Bes esu johd un donn et koppeere un enfööje", diff --git a/src/locales/mk.json b/src/locales/mk.json index 9fc6b817c..a924875ad 100644 --- a/src/locales/mk.json +++ b/src/locales/mk.json @@ -34,6 +34,7 @@ "pad.settings.padSettings": "Поставки на тетратката", "pad.settings.myView": "Мој поглед", "pad.settings.stickychat": "Разговорите секогаш на екранот", + "pad.settings.chatandusers": "Прикажи разговор и корисници", "pad.settings.colorcheck": "Авторски бои", "pad.settings.linenocheck": "Броеви на редовите", "pad.settings.rtlcheck": "Содржините да се читаат од десно на лево?", @@ -107,6 +108,7 @@ "timeslider.month.december": "декември", "timeslider.unnamedauthors": "{{num}} {[plural(num) one: неименуван автор, other: неименувани автори ]}", "pad.savedrevs.marked": "Оваа преработка сега е означена како зачувана", + "pad.savedrevs.timeslider": "Можете да ги погледате зачуваните преработки посетувајќи го времеследниот лизгач", "pad.userlist.entername": "Внесете го вашето име", "pad.userlist.unnamed": "без име", "pad.userlist.guest": "Гостин", diff --git a/src/locales/nl.json b/src/locales/nl.json index ed69527e3..183d0fa98 100644 --- a/src/locales/nl.json +++ b/src/locales/nl.json @@ -28,12 +28,14 @@ "pad.colorpicker.save": "Opslaan", "pad.colorpicker.cancel": "Annuleren", "pad.loading": "Bezig met laden…", + "pad.noCookie": "Er kon geen cookie gevonden worden. Zorg ervoor dat uw browser cookies accepteert.", "pad.passwordRequired": "U hebt een wachtwoord nodig om toegang te krijgen tot deze pad", "pad.permissionDenied": "U hebt geen rechten om deze pad te bekijken", "pad.wrongPassword": "U hebt een onjuist wachtwoord ingevoerd", "pad.settings.padSettings": "Padinstellingen", "pad.settings.myView": "Mijn overzicht", "pad.settings.stickychat": "Chat altijd zichtbaar", + "pad.settings.chatandusers": "Chat en gebruikers weergeven", "pad.settings.colorcheck": "Kleuren auteurs", "pad.settings.linenocheck": "Regelnummers", "pad.settings.rtlcheck": "Inhoud van rechts naar links lezen?", @@ -46,6 +48,7 @@ "pad.importExport.import": "Upload een tekstbestand of document", "pad.importExport.importSuccessful": "Afgerond", "pad.importExport.export": "Huidige pad exporteren als", + "pad.importExport.exportetherpad": "Etherpad", "pad.importExport.exporthtml": "HTML", "pad.importExport.exportplain": "Tekst zonder opmaak", "pad.importExport.exportword": "Microsoft Word", @@ -106,6 +109,7 @@ "timeslider.month.december": "december", "timeslider.unnamedauthors": "{{num}} onbekende {[plural(num) one: auteur, other: auteurs ]}", "pad.savedrevs.marked": "Deze versie is nu gemarkeerd als opgeslagen versie", + "pad.savedrevs.timeslider": "U kunt opgeslagen versies bekijken via de tijdschuiver.", "pad.userlist.entername": "Geef uw naam op", "pad.userlist.unnamed": "zonder naam", "pad.userlist.guest": "Gast", @@ -116,6 +120,7 @@ "pad.impexp.importing": "Bezig met importeren…", "pad.impexp.confirmimport": "Door een bestand te importeren overschrijft u de huidige tekst van de pad. Wilt u echt doorgaan?", "pad.impexp.convertFailed": "Het was niet mogelijk dit bestand te importeren. Gebruik een andere documentopmaak of kopieer en plak de inhoud handmatig", + "pad.impexp.padHasData": "Het was niet mogelijk dit bestand te importeren omdat er al wijzigingen aan de etherpad zijn gemaakt. Importeer naar een nieuwe etherpad.", "pad.impexp.uploadFailed": "Het uploaden is mislukt. Probeer het opnieuw", "pad.impexp.importfailed": "Importeren is mislukt", "pad.impexp.copypaste": "Gebruik kopiëren en plakken", diff --git a/src/locales/pt-br.json b/src/locales/pt-br.json index e8eb79eef..1fd145bc2 100644 --- a/src/locales/pt-br.json +++ b/src/locales/pt-br.json @@ -11,7 +11,8 @@ "Dianakc", "Macofe", "Rodrigo codignoli", - "Webysther" + "Webysther", + "Fasouzafreitas" ] }, "index.newPad": "Nova Nota", @@ -43,6 +44,7 @@ "pad.settings.padSettings": "Configurações da Nota", "pad.settings.myView": "Minha Visão", "pad.settings.stickychat": "Conversa sempre visível", + "pad.settings.chatandusers": "Mostrar o chat e os usuários", "pad.settings.colorcheck": "Cores de autoria", "pad.settings.linenocheck": "Números de linha", "pad.settings.rtlcheck": "Ler conteúdo da direita para esquerda?", @@ -116,6 +118,7 @@ "timeslider.month.december": "Dezembro", "timeslider.unnamedauthors": "{{num}} {[plural(num) one: autor anônimo, other: autores anônimos ]}", "pad.savedrevs.marked": "Esta revisão foi marcada como salva", + "pad.savedrevs.timeslider": "Pode consultar as revisões salvas visitando a linha do tempo", "pad.userlist.entername": "Insira o seu nome", "pad.userlist.unnamed": "Sem título", "pad.userlist.guest": "Convidado", diff --git a/src/locales/ru.json b/src/locales/ru.json index f96f2338c..bd2143a19 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -48,6 +48,7 @@ "pad.importExport.import": "Загрузить любой текстовый файл или документ", "pad.importExport.importSuccessful": "Успешно!", "pad.importExport.export": "Экспортировать текущий документ как:", + "pad.importExport.exportetherpad": "Etherpad", "pad.importExport.exporthtml": "HTML", "pad.importExport.exportplain": "Обычный текст", "pad.importExport.exportword": "Microsoft Word", diff --git a/src/locales/sr-ec.json b/src/locales/sr-ec.json new file mode 100644 index 000000000..9fd20b189 --- /dev/null +++ b/src/locales/sr-ec.json @@ -0,0 +1,59 @@ +{ + "@metadata": { + "authors": [ + "Aktron", + "Milicevic01", + "Милан Јелисавчић" + ] + }, + "index.newPad": "Нови Пад", + "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.indent.title": "Увлачење (TAB)", + "pad.toolbar.undo.title": "Опозови (Ctrl+Z)", + "pad.toolbar.settings.title": "Подешавања", + "pad.colorpicker.save": "Сачувај", + "pad.colorpicker.cancel": "Откажи", + "pad.loading": "Учитавање...", + "pad.wrongPassword": "Ваша лозинка није исправна", + "pad.settings.myView": "Мој приказ", + "pad.settings.fontType": "Врста фонта:", + "pad.settings.fontType.normal": "Нормално", + "pad.settings.fontType.monospaced": "Monospace", + "pad.settings.globalView": "Глобални приказ", + "pad.settings.language": "Језик:", + "pad.importExport.import_export": "Увоз/извоз", + "pad.importExport.import": "Отпремите било коју текстуалну датотеку или документ", + "pad.importExport.importSuccessful": "Успело!", + "pad.importExport.exporthtml": "HTML", + "pad.importExport.exportplain": "чист текст", + "pad.importExport.exportpdf": "PDF", + "pad.modals.connected": "Повезано.", + "pad.modals.slowcommit.explanation": "Сервер не одговара.", + "pad.modals.deleted": "Обрисано.", + "pad.share": "Дели овај пад", + "pad.share.readonly": "Само за читање", + "pad.share.link": "Веза", + "pad.chat": "Ћаскање", + "pad.chat.title": "Отворите ћаскање за овај пад.", + "pad.chat.loadmessages": "Учитајте више порука.", + "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.approve": "одобрено", + "pad.impexp.importbutton": "Увези одмах", + "pad.impexp.importing": "Увожење..." +} diff --git a/src/locales/sv.json b/src/locales/sv.json index a53146bf6..ae9b1f9a0 100644 --- a/src/locales/sv.json +++ b/src/locales/sv.json @@ -35,6 +35,7 @@ "pad.settings.padSettings": "Blockinställningar", "pad.settings.myView": "Min vy", "pad.settings.stickychat": "Chatten alltid på skärmen", + "pad.settings.chatandusers": "Visa chatt och användare", "pad.settings.colorcheck": "Författarskapsfärger", "pad.settings.linenocheck": "Radnummer", "pad.settings.rtlcheck": "Vill du läsa innehållet från höger till vänster?", @@ -108,6 +109,7 @@ "timeslider.month.december": "december", "timeslider.unnamedauthors": "{{num}} {[plural(num) one: namnlös författare, other: namnlösa författare]}", "pad.savedrevs.marked": "Denna version är nu markerad som en sparad version", + "pad.savedrevs.timeslider": "Du kan se sparade versioner med tidsreglaget", "pad.userlist.entername": "Ange ditt namn", "pad.userlist.unnamed": "namnlös", "pad.userlist.guest": "Gäst", diff --git a/src/locales/zh-hans.json b/src/locales/zh-hans.json index 10727c5d3..4af3cb48b 100644 --- a/src/locales/zh-hans.json +++ b/src/locales/zh-hans.json @@ -42,6 +42,7 @@ "pad.settings.padSettings": "记事本设置", "pad.settings.myView": "我的视窗", "pad.settings.stickychat": "总是显示聊天屏幕", + "pad.settings.chatandusers": "显示聊天和用户", "pad.settings.colorcheck": "作者颜色", "pad.settings.linenocheck": "行号", "pad.settings.rtlcheck": "从右到左阅读内容吗?", @@ -100,7 +101,7 @@ "timeslider.exportCurrent": "当前版本导出为:", "timeslider.version": "版本 {{version}}", "timeslider.saved": "在{{year}}年{{month}}{{day}}日保存", - "timeslider.dateformat": "{{year}}年{{month}}{{day}}日 {{hours}}时:{{minutes}}分:{{seconds}}秒", + "timeslider.dateformat": "{{year}}年{{month}}月{{day}}日 {{hours}}时:{{minutes}}分:{{seconds}}秒", "timeslider.month.january": "1月", "timeslider.month.february": "2月", "timeslider.month.march": "三月", @@ -115,6 +116,7 @@ "timeslider.month.december": "十二月", "timeslider.unnamedauthors": "{{num}}个匿名作者", "pad.savedrevs.marked": "这一修订现在被标记为已保存的修订版本", + "pad.savedrevs.timeslider": "您可以使用时间滑块查阅已保存的版本", "pad.userlist.entername": "输入您的姓名", "pad.userlist.unnamed": "匿名", "pad.userlist.guest": "访客", From d5bec1701e10c2d0b5f1ffa96fd5ce4e86728e88 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 21 Feb 2015 12:33:30 +0000 Subject: [PATCH 360/455] fix export of bad pads and also limit import to files --- src/node/utils/ExportEtherpad.js | 2 +- src/node/utils/ImportEtherpad.js | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/node/utils/ExportEtherpad.js b/src/node/utils/ExportEtherpad.js index 4f91e4e39..b7d438523 100644 --- a/src/node/utils/ExportEtherpad.js +++ b/src/node/utils/ExportEtherpad.js @@ -48,7 +48,7 @@ exports.getPadRaw = function(padId, callback){ // Get the author info db.get("globalAuthor:"+authorId, function(e, authorEntry){ - authorEntry.padIDs = padId; + if(authorEntry && authorEntry.padIDs) authorEntry.padIDs = padId; if(!e) data["globalAuthor:"+authorId] = authorEntry; }); diff --git a/src/node/utils/ImportEtherpad.js b/src/node/utils/ImportEtherpad.js index 1574a3a9d..37863bfff 100644 --- a/src/node/utils/ImportEtherpad.js +++ b/src/node/utils/ImportEtherpad.js @@ -21,9 +21,22 @@ var db = require("../db/DB").db; exports.setPadRaw = function(padId, records, callback){ records = JSON.parse(records); + // !! HACK !! + // If you have a really large pad it will cause a Maximum Range Stack crash + // This is a temporary patch for that so things are kept stable. + var recordCount = Object.keys(records).length; + if(recordCount >= 50000){ + console.warn("Etherpad file is too large to import.. We need to fix this. See https://github.com/ether/etherpad-lite/issues/2524"); + return callback("tooLarge", false); + } + async.eachSeries(Object.keys(records), function(key, cb){ var value = records[key] + if(!value){ + cb(); // null values are bad. + } + // Author data if(value.padIDs){ // rewrite author pad ids @@ -34,7 +47,9 @@ exports.setPadRaw = function(padId, records, callback){ db.get(key, function(err, author){ if(author){ // Yes, add the padID to the author.. - author.padIDs.push(padId); + if( Object.prototype.toString.call(author) === '[object Array]'){ + author.padIDs.push(padId); + } value = author; }else{ // No, create a new array with the author info in From 1c044588e6d9b0ee721f5c68e94c480028ce6467 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 22 Feb 2015 17:56:44 +0000 Subject: [PATCH 361/455] mobile stylings for chat always on screen --- src/static/css/pad.css | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/static/css/pad.css b/src/static/css/pad.css index 5e4c4d14e..c9ebff4a5 100644 --- a/src/static/css/pad.css +++ b/src/static/css/pad.css @@ -1014,12 +1014,16 @@ input[type=checkbox] { top: 72px !important; } } + +/* Mobile devices */ @media only screen and (min-device-width: 320px) and (max-device-width: 720px) { #users { top: auto; right:0px !important; bottom: 33px; border-radius: 0px !important; + height: 55px !important; + overflow: auto; } #mycolorpicker { left: -73px; @@ -1099,7 +1103,8 @@ input[type=checkbox] { } #chatbox{ position:absolute; - bottom:33px; + bottom:33px !important; + margin: 65px 0 0 0; } #gritter-notice-wrapper{ bottom:43px !important; From 4166f190780f196a24248ec2733e97d7c0ba76ec Mon Sep 17 00:00:00 2001 From: Luc Didry Date: Sun, 22 Feb 2015 22:32:18 +0100 Subject: [PATCH 362/455] Set correct API version in doc The doc says current version is 1.2.9, but there is getPadID which uses 1.2.10 API. --- doc/api/http_api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/http_api.md b/doc/api/http_api.md index 6cbe6e6b6..84a4ec7e2 100644 --- a/doc/api/http_api.md +++ b/doc/api/http_api.md @@ -61,7 +61,7 @@ Portal submits content into new blog post ## Usage ### API version -The latest version is `1.2.9` +The latest version is `1.2.10` The current version can be queried via /api. From a08c50a77dc897009f9e40d9d0bdab0a219d59fa Mon Sep 17 00:00:00 2001 From: Luc Didry Date: Tue, 24 Feb 2015 23:42:35 +0100 Subject: [PATCH 363/455] Fixes #1870 Add two functions to API : * getSavedRevisionsCount * listSavedRevisions --- doc/api/http_api.md | 20 +++++++++++++++++- src/node/db/API.js | 38 ++++++++++++++++++++++++++++++++++ src/node/db/Pad.js | 12 +++++++++++ src/node/handler/APIHandler.js | 2 ++ 4 files changed, 71 insertions(+), 1 deletion(-) diff --git a/doc/api/http_api.md b/doc/api/http_api.md index 84a4ec7e2..d6432e3cd 100644 --- a/doc/api/http_api.md +++ b/doc/api/http_api.md @@ -61,7 +61,7 @@ Portal submits content into new blog post ## Usage ### API version -The latest version is `1.2.10` +The latest version is `1.2.11` The current version can be queried via /api. @@ -402,6 +402,24 @@ returns the number of revisions of this pad * `{code: 0, message:"ok", data: {revisions: 56}}` * `{code: 1, message:"padID does not exist", data: null}` +#### getSavedRevisionsCount(padID) + * API >= 1.2.11 + +returns the number of saved revisions of this pad + +*Example returns:* + * `{code: 0, message:"ok", data: {savedRevisions: 42}}` + * `{code: 1, message:"padID does not exist", data: null}` + +#### listSavedRevisions(padID) + * API >= 1.2.11 + +returns the list of saved revisions of this pad + +*Example returns:* + * `{code: 0, message:"ok", data: {savedRevisions: [2, 42, 1337]}}` + * `{code: 1, message:"padID does not exist", data: null}` + #### padUsersCount(padID) * API >= 1 diff --git a/src/node/db/API.js b/src/node/db/API.js index 81dedcfef..ab8a1d405 100644 --- a/src/node/db/API.js +++ b/src/node/db/API.js @@ -517,6 +517,44 @@ exports.getRevisionsCount = function(padID, callback) }); } +/** +getSavedRevisionsCount(padID) returns the number of saved revisions of this pad + +Example returns: + +{code: 0, message:"ok", data: {savedRevisions: 42}} +{code: 1, message:"padID does not exist", data: null} +*/ +exports.getSavedRevisionsCount = function(padID, callback) +{ + //get the pad + getPadSafe(padID, true, function(err, pad) + { + if(ERR(err, callback)) return; + + callback(null, {savedRevisions: pad.getSavedRevisionsNumber()}); + }); +} + +/** +listSavedRevisions(padID) returns the list of saved revisions of this pad + +Example returns: + +{code: 0, message:"ok", data: {savedRevisions: [2, 42, 1337]}} +{code: 1, message:"padID does not exist", data: null} +*/ +exports.listSavedRevisions = function(padID, callback) +{ + //get the pad + getPadSafe(padID, true, function(err, pad) + { + if(ERR(err, callback)) return; + + callback(null, {savedRevisions: pad.getSavedRevisionsList()}); + }); +} + /** getLastEdited(padID) returns the timestamp of the last revision of the pad diff --git a/src/node/db/Pad.js b/src/node/db/Pad.js index 2f5860f8f..7a02e0fbd 100644 --- a/src/node/db/Pad.js +++ b/src/node/db/Pad.js @@ -54,6 +54,18 @@ Pad.prototype.getHeadRevisionNumber = function getHeadRevisionNumber() { return this.head; }; +Pad.prototype.getSavedRevisionsNumber = function getSavedRevisionsNumber() { + return this.savedRevisions.length; +}; + +Pad.prototype.getSavedRevisionsList = function getSavedRevisionsList() { + var savedRev = new Array(); + for(var rev in this.savedRevisions){ + savedRev.push(this.savedRevisions[rev].revNum); + } + return savedRev; +}; + Pad.prototype.getPublicStatus = function getPublicStatus() { return this.publicStatus; }; diff --git a/src/node/handler/APIHandler.js b/src/node/handler/APIHandler.js index a26dd2cfb..a68a4472d 100644 --- a/src/node/handler/APIHandler.js +++ b/src/node/handler/APIHandler.js @@ -368,6 +368,8 @@ var version = , "setHTML" : ["padID", "html"] , "getAttributePool" : ["padID"] , "getRevisionsCount" : ["padID"] + , "getSavedRevisionsCount" : ["padID"] + , "listSavedRevisions" : ["padID"] , "getRevisionChangeset" : ["padID", "rev"] , "getLastEdited" : ["padID"] , "deletePad" : ["padID"] From 845788c39df0b9674c516e7673a8ba7bb43495b1 Mon Sep 17 00:00:00 2001 From: Luc Didry Date: Wed, 25 Feb 2015 01:04:27 +0100 Subject: [PATCH 364/455] Add a saveRevision API function Calling saveRevision create an author which name is "API" --- doc/api/http_api.md | 9 +++++ src/node/db/API.js | 73 ++++++++++++++++++++++++++++++++++ src/node/handler/APIHandler.js | 1 + 3 files changed, 83 insertions(+) diff --git a/doc/api/http_api.md b/doc/api/http_api.md index d6432e3cd..2ae674d8c 100644 --- a/doc/api/http_api.md +++ b/doc/api/http_api.md @@ -420,6 +420,15 @@ returns the list of saved revisions of this pad * `{code: 0, message:"ok", data: {savedRevisions: [2, 42, 1337]}}` * `{code: 1, message:"padID does not exist", data: null}` +#### saveRevision(padID [, rev]) + * API >= 1.2.11 + +saves a revision + +*Example returns:* + * `{code: 0, message:"ok", data: null}` + * `{code: 1, message:"padID does not exist", data: null}` + #### padUsersCount(padID) * API >= 1 diff --git a/src/node/db/API.js b/src/node/db/API.js index ab8a1d405..69a380c7f 100644 --- a/src/node/db/API.js +++ b/src/node/db/API.js @@ -555,6 +555,79 @@ exports.listSavedRevisions = function(padID, callback) }); } +/** +saveRevision(padID) returns the list of saved revisions of this pad + +Example returns: + +{code: 0, message:"ok", data: null} +{code: 1, message:"padID does not exist", data: null} +*/ +exports.saveRevision = function(padID, rev, callback) +{ + //check if rev is set + if(typeof rev == "function") + { + callback = rev; + rev = undefined; + } + + //check if rev is a number + if(rev !== undefined && typeof rev != "number") + { + //try to parse the number + if(!isNaN(parseInt(rev))) + { + rev = parseInt(rev); + } + else + { + callback(new customError("rev is not a number", "apierror")); + return; + } + } + + //ensure this is not a negativ number + if(rev !== undefined && rev < 0) + { + callback(new customError("rev is a negativ number","apierror")); + return; + } + + //ensure this is not a float value + if(rev !== undefined && !is_int(rev)) + { + callback(new customError("rev is a float value","apierror")); + return; + } + + //get the pad + getPadSafe(padID, true, function(err, pad) + { + if(ERR(err, callback)) return; + + //the client asked for a special revision + if(rev !== undefined) + { + //check if this is a valid revision + if(rev > pad.getHeadRevisionNumber()) + { + callback(new customError("rev is higher than the head revision of the pad","apierror")); + return; + } + } else { + rev = pad.getHeadRevisionNumber(); + } + + authorManager.createAuthor('API', function(err, author) { + if(ERR(err, callback)) return; + + pad.addSavedRevision(rev, author.authorID, 'Saved through API call'); + callback(); + }); + }); +} + /** getLastEdited(padID) returns the timestamp of the last revision of the pad diff --git a/src/node/handler/APIHandler.js b/src/node/handler/APIHandler.js index a68a4472d..232b0b466 100644 --- a/src/node/handler/APIHandler.js +++ b/src/node/handler/APIHandler.js @@ -370,6 +370,7 @@ var version = , "getRevisionsCount" : ["padID"] , "getSavedRevisionsCount" : ["padID"] , "listSavedRevisions" : ["padID"] + , "saveRevision" : ["padID", "rev"] , "getRevisionChangeset" : ["padID", "rev"] , "getLastEdited" : ["padID"] , "deletePad" : ["padID"] From 92022e493ee882f486e2ad9e5e0986b1af90f614 Mon Sep 17 00:00:00 2001 From: Luc Didry Date: Wed, 25 Feb 2015 01:05:58 +0100 Subject: [PATCH 365/455] Add backend tests for new API functions These new functions are: * getSavedRevisionsCount * listSavedRevisions * saveRevision + typo fixing in backend tests --- src/node/db/Pad.js | 3 + tests/backend/specs/api/pad.js | 143 ++++++++++++++++++++++++++------- 2 files changed, 117 insertions(+), 29 deletions(-) diff --git a/src/node/db/Pad.js b/src/node/db/Pad.js index 7a02e0fbd..538476002 100644 --- a/src/node/db/Pad.js +++ b/src/node/db/Pad.js @@ -63,6 +63,9 @@ Pad.prototype.getSavedRevisionsList = function getSavedRevisionsList() { for(var rev in this.savedRevisions){ savedRev.push(this.savedRevisions[rev].revNum); } + savedRev.sort(function(a, b) { + return a - b; + }); return savedRev; }; diff --git a/tests/backend/specs/api/pad.js b/tests/backend/specs/api/pad.js index 6010a11ce..52849c2ea 100644 --- a/tests/backend/specs/api/pad.js +++ b/tests/backend/specs/api/pad.js @@ -48,33 +48,38 @@ describe('Permission', function(){ -> deletePad -- This gives us a guaranteed clear environment -> createPad -> 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 - -> 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 - -> listAuthorsOfPad(padID) -- should be empty array? - -> 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 - - -> setText(padId, "hello world") + -> getSavedRevisionsCount(padID) -- Should be 0 + -> listSavedRevisions(padID) -- Should be an empty array + -> 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? + -> saveRevision + -> getSavedRevisionsCount(padID) -- Should be 0 still? + -> listSavedRevisions(padID) -- Should be an empty array still ? + -> padUsersCount -- Should be 0 + -> getReadOnlyId -- Should be a value + -> listAuthorsOfPad(padID) -- should be empty array? -> 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 - -> setHTML(padID) -- Should fail on invalid HTML - -> setHTML(padID) *3 -- Should fail on invalid HTML - -> getHTML(padID) -- Should return HTML close to posted HTML + -> 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 + -> setHTML(padID) -- Should fail on invalid HTML + -> setHTML(padID) *3 -- Should fail on invalid HTML + -> getHTML(padID) -- Should return HTML close to posted HTML */ @@ -109,11 +114,35 @@ describe('getRevisionsCount', function(){ }); }) +describe('getSavedRevisionsCount', function(){ + it('gets saved revisions count of Pad', function(done) { + api.get(endPoint('getSavedRevisionsCount')+"&padID="+testPadId) + .expect(function(res){ + if(res.body.code !== 0) throw new Error("Unable to get Saved Revisions Count"); + if(res.body.data.savedRevisions !== 0) throw new Error("Incorrect Saved Revisions Count"); + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('listSavedRevisions', function(){ + it('gets saved revision list of Pad', function(done) { + api.get(endPoint('listSavedRevisions')+"&padID="+testPadId) + .expect(function(res){ + if(res.body.code !== 0) throw new Error("Unable to get Saved Revisions List"); + if(!res.body.data.savedRevisions.equals([])) throw new Error("Incorrect Saved Revisions List"); + }) + .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(function(res){ - if(res.body.data.html.length <= 1) throw new Error("Unable to get Revision Count"); + if(res.body.data.html.length <= 1) throw new Error("Unable to get the HTML"); }) .expect('Content-Type', /json/) .expect(200, done) @@ -187,16 +216,50 @@ describe('getText', function(){ }) describe('getRevisionsCount', function(){ - it('gets Revision Coutn of a Pad', function(done) { + it('gets Revision Count of a Pad', function(done) { api.get(endPoint('getRevisionsCount')+"&padID="+testPadId) .expect(function(res){ - if(res.body.data.revisions !== 1) throw new Error("Unable to set text revision count") + if(res.body.data.revisions !== 1) throw new Error("Unable to get text revision count") }) .expect('Content-Type', /json/) .expect(200, done) }); }) +describe('saveRevision', function(){ + it('saves Revision', function(done) { + api.get(endPoint('saveRevision')+"&padID="+testPadId) + .expect(function(res){ + if(res.body.code !== 0) throw new Error("Unable to save Revision"); + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('getSavedRevisionsCount', function(){ + it('gets saved revisions count of Pad', function(done) { + api.get(endPoint('getSavedRevisionsCount')+"&padID="+testPadId) + .expect(function(res){ + if(res.body.code !== 0) throw new Error("Unable to get Saved Revisions Count"); + if(res.body.data.savedRevisions !== 1) throw new Error("Incorrect Saved Revisions Count"); + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('listSavedRevisions', function(){ + it('gets saved revision list of Pad', function(done) { + api.get(endPoint('listSavedRevisions')+"&padID="+testPadId) + .expect(function(res){ + if(res.body.code !== 0) throw new Error("Unable to get Saved Revisions List"); + if(!res.body.data.savedRevisions.equals([1])) throw new Error("Incorrect Saved Revisions List"); + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) describe('padUsersCount', function(){ it('gets User Count of a Pad', function(done) { api.get(endPoint('padUsersCount')+"&padID="+testPadId) @@ -461,3 +524,25 @@ function generateLongText(){ } return text; } + +// Need this to compare arrays (listSavedRevisions test) +Array.prototype.equals = function (array) { + // if the other array is a falsy value, return + if (!array) + return false; + // compare lengths - can save a lot of time + if (this.length != array.length) + return false; + for (var i = 0, l=this.length; i < l; i++) { + // Check if we have nested arrays + if (this[i] instanceof Array && array[i] instanceof Array) { + // recurse into the nested arrays + if (!this[i].equals(array[i])) + return false; + } else if (this[i] != array[i]) { + // Warning - two different object instances will never be equal: {x:20} != {x:20} + return false; + } + } + return true; +} From 38a4f1be5f08784d94080adf55f83d5f146364a1 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 26 Feb 2015 12:14:01 +0000 Subject: [PATCH 366/455] bump ueberdb --- src/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/package.json b/src/package.json index 00876f2cf..815d865fc 100644 --- a/src/package.json +++ b/src/package.json @@ -17,7 +17,7 @@ "etherpad-require-kernel" : "1.0.8", "resolve" : "1.1.0", "socket.io" : "1.3.3", - "ueberDB" : "0.2.11", + "ueberDB" : "0.2.12", "express" : "3.8.1", "async" : "0.9.0", "connect" : "2.7.11", From 7719117e1e1b0d4d7b1d3740329cbea92258c6f8 Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Thu, 26 Feb 2015 14:57:49 +0100 Subject: [PATCH 367/455] do not crash when encountering mismatched compositions. log the changesets and padid --- src/node/handler/PadMessageHandler.js | 12 ++++++++---- src/static/js/Changeset.js | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index 7ea5039d8..86244d85d 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -1629,10 +1629,14 @@ function composePadChangesets(padId, startNum, endNum, callback) changeset = changesets[startNum]; var pool = pad.apool(); - for(var r=startNum+1;r Date: Fri, 27 Feb 2015 12:54:29 -0500 Subject: [PATCH 368/455] Trigger renumbering when deleting (via cut) the first item of a list Fixes #2514. --- src/static/js/ace2_inner.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 4b84e784d..15747249d 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -3313,6 +3313,14 @@ function Ace2Inner(){ return [rep.lines.offsetOfIndex(lineRange[0]), rep.lines.offsetOfIndex(lineRange[1])]; } + function handleCut(evt) + { + inCallStackIfNecessary("handleCut", function() + { + doDeleteKey(evt); + }); + } + function handleClick(evt) { inCallStackIfNecessary("handleClick", function() @@ -4854,6 +4862,7 @@ function Ace2Inner(){ $(document).on("keypress", handleKeyEvent); $(document).on("keyup", handleKeyEvent); $(document).on("click", handleClick); + $(document).on("cut", handleCut); $(root).on("blur", handleBlur); if (browser.msie) { From 4e0353b3ef4e5b43278695c9a571c933c314e713 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 2 Mar 2015 08:59:53 +0000 Subject: [PATCH 369/455] bump ueber --- src/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/package.json b/src/package.json index 815d865fc..e2d9e2996 100644 --- a/src/package.json +++ b/src/package.json @@ -17,7 +17,7 @@ "etherpad-require-kernel" : "1.0.8", "resolve" : "1.1.0", "socket.io" : "1.3.3", - "ueberDB" : "0.2.12", + "ueberDB" : "0.2.13", "express" : "3.8.1", "async" : "0.9.0", "connect" : "2.7.11", From 683c9dfb7cc01ef46d6f783102c6acb4b32255dd Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Mon, 2 Mar 2015 11:05:33 +0100 Subject: [PATCH 370/455] callback with argument error in async.series instead --- 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 86244d85d..e585b6528 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -1636,7 +1636,7 @@ function composePadChangesets(padId, startNum, endNum, callback) } } catch(e){ console.warn("failed to compose cs in pad:",padId); - return; + return callback(e); } callback(null); From 0f82cd871141a4ca465e171dd4eb0fe6a73058f6 Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Mon, 2 Mar 2015 11:14:24 +0100 Subject: [PATCH 371/455] print revision numbers - not changesets - in warn-log --- src/node/handler/PadMessageHandler.js | 3 ++- src/static/js/Changeset.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index e585b6528..ac8904f8d 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -1635,7 +1635,8 @@ function composePadChangesets(padId, startNum, endNum, callback) changeset = Changeset.compose(changeset, cs, pool); } } catch(e){ - console.warn("failed to compose cs in pad:",padId); + // r-1 indicates the rev that was build starting with startNum, applying startNum+1, +2, +3 + console.warn("failed to compose cs in pad:",padId," startrev:",startNum," current rev:",r); return callback(e); } diff --git a/src/static/js/Changeset.js b/src/static/js/Changeset.js index c97756b8d..df180f9cf 100644 --- a/src/static/js/Changeset.js +++ b/src/static/js/Changeset.js @@ -1318,7 +1318,7 @@ exports.compose = function (cs1, cs2, pool) { var unpacked2 = exports.unpack(cs2); var len1 = unpacked1.oldLen; var len2 = unpacked1.newLen; - exports.assert(len2 == unpacked2.oldLen, "mismatched composition of two changesets - cs1:",cs1," and cs2:",cs2); + exports.assert(len2 == unpacked2.oldLen, "mismatched composition of two changesets"); var len3 = unpacked2.newLen; var bankIter1 = exports.stringIterator(unpacked1.charBank); var bankIter2 = exports.stringIterator(unpacked2.charBank); From 01cd82427a4e83e656f084923f8f06a1a61582c9 Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Tue, 3 Mar 2015 15:20:33 +0100 Subject: [PATCH 372/455] check author in = operator --- src/node/handler/PadMessageHandler.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index 7ea5039d8..7521c05c4 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -656,7 +656,12 @@ function handleUserChanges(data, cb) , op while(iterator.hasNext()) { op = iterator.next() - if(op.opcode != '+') continue; + + //+ can add text with attribs + //= can change or add attribs + //- can have attribs, but they are discarded and don't show up in the apool + if(op.opcode == '-') continue; + op.attribs.split('*').forEach(function(attr) { if(!attr) return attr = wireApool.getAttrib(attr) From 0693c0ae970627583f435a0e8a70ab4639b9a2f8 Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Tue, 3 Mar 2015 15:37:56 +0100 Subject: [PATCH 373/455] - operator do not show up in the attribs of a pad, but authors could still leak to the pool --- src/node/handler/PadMessageHandler.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index 7521c05c4..ab81ad878 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -659,8 +659,7 @@ function handleUserChanges(data, cb) //+ can add text with attribs //= can change or add attribs - //- can have attribs, but they are discarded and don't show up in the apool - if(op.opcode == '-') continue; + //- can have attribs, but they are discarded and don't show up in the attribs - but do show up in the pool op.attribs.split('*').forEach(function(attr) { if(!attr) return From 393a4e54e5517624f07299a436e623a34f8104e2 Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Tue, 3 Mar 2015 16:17:39 +0100 Subject: [PATCH 374/455] recognize reconnect in clear_authorship_colors test --- tests/frontend/specs/clear_authorship_colors.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/frontend/specs/clear_authorship_colors.js b/tests/frontend/specs/clear_authorship_colors.js index 5db35612a..41fabe3cf 100644 --- a/tests/frontend/specs/clear_authorship_colors.js +++ b/tests/frontend/specs/clear_authorship_colors.js @@ -47,6 +47,10 @@ describe("clear authorship colors button", function(){ var hasAuthorClass = inner$("div").first().attr("class").indexOf("author") !== -1; expect(hasAuthorClass).to.be(false); + + var disconnectVisible = chrome$("div.disconnected").attr("class").indexOf("visible") === -1 + expect(disconnectVisible).to.be(false); + done(); }); From f249b42ab43c45f255c098c0a9cc904e43b0305a Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Tue, 3 Mar 2015 16:39:14 +0100 Subject: [PATCH 375/455] empty author should be allowed to support clearAuthorship functionality --- src/node/handler/PadMessageHandler.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index ab81ad878..ef8e32b0b 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -665,7 +665,8 @@ function handleUserChanges(data, cb) if(!attr) return attr = wireApool.getAttrib(attr) if(!attr) return - if('author' == attr[0] && attr[1] != thisSession.author) throw new Error("Trying to submit changes as another author in changeset "+changeset); + //the empty author is used in the clearAuthorship functionality so this should be the only exception + if('author' == attr[0] && (attr[1] != thisSession.author && attr[1] != '')) throw new Error("Trying to submit changes as another author in changeset "+changeset); }) } From 547046830e0808b86dcde94b4e8f6da9206ddb89 Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Tue, 3 Mar 2015 16:51:18 +0100 Subject: [PATCH 376/455] actually disconnect should NOT be visible... --- tests/frontend/specs/clear_authorship_colors.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/frontend/specs/clear_authorship_colors.js b/tests/frontend/specs/clear_authorship_colors.js index 41fabe3cf..1417f63c6 100644 --- a/tests/frontend/specs/clear_authorship_colors.js +++ b/tests/frontend/specs/clear_authorship_colors.js @@ -47,9 +47,10 @@ describe("clear authorship colors button", function(){ var hasAuthorClass = inner$("div").first().attr("class").indexOf("author") !== -1; expect(hasAuthorClass).to.be(false); - - var disconnectVisible = chrome$("div.disconnected").attr("class").indexOf("visible") === -1 - expect(disconnectVisible).to.be(false); + setTimeout(function(){ + var disconnectVisible = chrome$("div.disconnected").attr("class").indexOf("visible") === -1 + expect(disconnectVisible).to.be(true); + },1000); done(); }); From 56dbad41ada0e1d848fb8fca4a1608d34b372b36 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 4 Mar 2015 17:46:49 +0000 Subject: [PATCH 377/455] a should always have white space pre-wrap stops caret walking in chrome on them --- src/static/css/iframe_editor.css | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/static/css/iframe_editor.css b/src/static/css/iframe_editor.css index 1f247e59a..eb69364bd 100644 --- a/src/static/css/iframe_editor.css +++ b/src/static/css/iframe_editor.css @@ -11,7 +11,10 @@ span { cursor: auto; } background: #acf; } -a { cursor: pointer !important; } +a { + cursor: pointer !important; + white-space:pre-wrap; +} ul, ol, li { padding: 0; From da1bf00a78406894d3840c8c0370aeb1c9900a53 Mon Sep 17 00:00:00 2001 From: Cristo Date: Fri, 6 Mar 2015 23:02:31 +0100 Subject: [PATCH 378/455] fixed + support for value --- src/static/js/AttributeManager.js | 57 +++++++++++++++---------------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/src/static/js/AttributeManager.js b/src/static/js/AttributeManager.js index 974d8ad91..fbfd6b309 100644 --- a/src/static/js/AttributeManager.js +++ b/src/static/js/AttributeManager.js @@ -153,37 +153,36 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ return this.applyChangeset(builder); }, - /* - Removes a specified attribute on a line - @param lineNum: the number of the affected line - @param attributeKey: the name of the attribute to remove, e.g. list - + /** + * Removes a specified attribute on a line + * @param lineNum the number of the affected line + * @param attributeName the name of the attribute to remove, e.g. list + * @param attributeValue if given only attributes with equal value will be removed */ removeAttributeOnLine: function(lineNum, attributeName, attributeValue){ - var loc = [0,0]; - var builder = Changeset.builder(this.rep.lines.totalWidth()); - var hasMarker = this.lineHasMarker(lineNum); - var attribs - var foundAttrib = false - - attribs = this.getAttributesOnLine(lineNum).map(function(attrib) { - if(attrib[0] === attributeName) { - foundAttrib = true - return [attributeName, null] // remove this attrib from the linemarker + var builder = Changeset.builder(this.rep.lines.totalWidth()); + var hasMarker = this.lineHasMarker(lineNum); + var found = false; + + var attribs = _(this.getAttributesOnLine(lineNum)).map(function (attrib) { + if (attrib[0] === attributeName && (!attributeValue || attrib[0] === attributeValue)){ + found = true; + return [attributeName, '']; + } + return attrib; + }); + + if (!found) { + return; + } + ChangesetUtils.buildKeepToStartOfRange(this.rep, builder, [lineNum, 0]); + var list = _.chain(attribs).filter(function(a){return !!a[1];}).map(function(a){return a[0];}).value(); + //if we have marker and any of attributes don't need to have marker. we need delete it + if(hasMarker && !_.intersection(lineAttributes,list)){ + ChangesetUtils.buildRemoveRange(this.rep, builder, [lineNum, 0], [lineNum, 1]); + }else{ + ChangesetUtils.buildKeepRange(this.rep, builder, [lineNum, 0], [lineNum, 1], attribs, this.rep.apool); } - return attrib - }) - - if(!foundAttrib) { - return - } - - 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])) - else ChangesetUtils.buildKeepRange(this.rep, builder, loc, (loc = [lineNum, 1]), attribs, this.rep.apool); - } return this.applyChangeset(builder); }, @@ -202,4 +201,4 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ } }); -module.exports = AttributeManager; \ No newline at end of file +module.exports = AttributeManager; From 7894545556226c4a17b45f6164d32041088cdb89 Mon Sep 17 00:00:00 2001 From: lid2000 Date: Fri, 13 Mar 2015 16:01:18 +1100 Subject: [PATCH 379/455] Return true from handleCut function --- 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 15747249d..aa4bf6c7d 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -3319,6 +3319,7 @@ function Ace2Inner(){ { doDeleteKey(evt); }); + return true; } function handleClick(evt) From a0fb65205c7d7ff95f00eb9fd88e93b300f30c3d Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Sat, 14 Mar 2015 00:02:23 +0100 Subject: [PATCH 380/455] oops, fix export with wildcards --- src/node/utils/ExportEtherpad.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/node/utils/ExportEtherpad.js b/src/node/utils/ExportEtherpad.js index b7d438523..46ae0d7af 100644 --- a/src/node/utils/ExportEtherpad.js +++ b/src/node/utils/ExportEtherpad.js @@ -23,9 +23,19 @@ exports.getPadRaw = function(padId, callback){ async.waterfall([ function(cb){ - // Get the Pad available content keys - db.findKeys("pad:"+padId+"*", null, function(err,records){ + // Get the Pad + db.findKeys("pad:"+padId, null, function(err,padcontent){ if(!err){ + cb(err, padcontent); + } + }) + }, + function(padcontent,cb){ + + // Get the Pad available content keys + db.findKeys("pad:"+padId+":*", null, function(err,records){ + if(!err){ + for (var key in padcontent) { records.push(padcontent[key]);} cb(err, records); } }) From c0260bcc40e93e9a47a20aada86a8513776f2e17 Mon Sep 17 00:00:00 2001 From: Stefan Date: Sun, 15 Mar 2015 14:28:47 +0100 Subject: [PATCH 381/455] Add changelog for v1.5.2 --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75a9f3276..bf27f2921 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +# 1.5.2 + * NEW: Support for node version 0.12.x + * NEW: API endpoint saveRevision, getSavedRevisionCount and listSavedRevisions + * NEW: setting to allow load testing + * Fix: Rare scroll issue + * Fix: Handling of custom pad path + * Fix: Better error handling of imports and exports of type "etherpad" + * Fix: Walking caret in chrome + * Fix: Better handling for changeset problems + * SECURITY Fix: Information leak for etherpad exports (CVE-2015-2298) + # 1.5.1 * NEW: High resolution Icon * NEW: Use HTTPS for plugins.json download From a3f07c1048a039e39b134afe5f767394f479841a Mon Sep 17 00:00:00 2001 From: Thomas Muehlichen Date: Fri, 20 Mar 2015 11:58:56 +0100 Subject: [PATCH 382/455] fixes #2556 (error toggling line attribute) and clarified method documentation (AttributeManager.toggleAttributeOnLine) --- src/static/js/AttributeManager.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/static/js/AttributeManager.js b/src/static/js/AttributeManager.js index 974d8ad91..865569c53 100644 --- a/src/static/js/AttributeManager.js +++ b/src/static/js/AttributeManager.js @@ -189,13 +189,15 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ }, /* - Sets a specified attribute on a line - @param lineNum: the number of the line to set the attribute for - @param attributeKey: the name of the attribute to set, e.g. list - @param attributeValue: an optional parameter to pass to the attribute (e.g. indention level) + Toggles a line attribute for the specified line number + If a line attribute with the specified name exists with any value it will be removed + Otherwise it will be set to the given value + @param lineNum: the number of the line to toggle the attribute for + @param attributeKey: the name of the attribute to toggle, e.g. list + @param attributeValue: the value to pass to the attribute (e.g. indention level) */ toggleAttributeOnLine: function(lineNum, attributeName, attributeValue) { - return this.getAttributeOnLine(attributeName) ? + return this.getAttributeOnLine(lineNum, attributeName) ? this.removeAttributeOnLine(lineNum, attributeName) : this.setAttributeOnLine(lineNum, attributeName, attributeValue); From 9bde17b91cadaf565f8f6021611b9fbcab05d005 Mon Sep 17 00:00:00 2001 From: Simon Gaeremynck Date: Sun, 22 Mar 2015 12:27:54 +0000 Subject: [PATCH 383/455] Bumped ueberDB to 0.2.14 so Cassandra suport is included --- src/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/package.json b/src/package.json index e2d9e2996..d7b71a8c4 100644 --- a/src/package.json +++ b/src/package.json @@ -17,7 +17,7 @@ "etherpad-require-kernel" : "1.0.8", "resolve" : "1.1.0", "socket.io" : "1.3.3", - "ueberDB" : "0.2.13", + "ueberDB" : "0.2.14", "express" : "3.8.1", "async" : "0.9.0", "connect" : "2.7.11", From 382804e44c88c96ad50d50788f9f0bdfcad8dd40 Mon Sep 17 00:00:00 2001 From: cristo-rabani Date: Sun, 22 Mar 2015 23:14:17 +0100 Subject: [PATCH 384/455] fix --- src/static/js/AttributeManager.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/static/js/AttributeManager.js b/src/static/js/AttributeManager.js index fbfd6b309..5e7823070 100644 --- a/src/static/js/AttributeManager.js +++ b/src/static/js/AttributeManager.js @@ -179,9 +179,9 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ var list = _.chain(attribs).filter(function(a){return !!a[1];}).map(function(a){return a[0];}).value(); //if we have marker and any of attributes don't need to have marker. we need delete it if(hasMarker && !_.intersection(lineAttributes,list)){ - ChangesetUtils.buildRemoveRange(this.rep, builder, [lineNum, 0], [lineNum, 1]); + ChangesetUtils.buildRemoveRange(this.rep, builder, [lineNum, 1], [lineNum, 2]); }else{ - ChangesetUtils.buildKeepRange(this.rep, builder, [lineNum, 0], [lineNum, 1], attribs, this.rep.apool); + ChangesetUtils.buildKeepRange(this.rep, builder, [lineNum, 1], [lineNum, 2], attribs, this.rep.apool); } return this.applyChangeset(builder); From 83e6591f1a48eb90688a21edbc03723b958a4ff4 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 24 Mar 2015 09:52:17 +0000 Subject: [PATCH 385/455] temp fix for cut paste but might break some OL functionality --- src/static/js/ace2_inner.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index aa4bf6c7d..4305e9b48 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -4863,7 +4863,8 @@ function Ace2Inner(){ $(document).on("keypress", handleKeyEvent); $(document).on("keyup", handleKeyEvent); $(document).on("click", handleClick); - $(document).on("cut", handleCut); + // $(document).on("cut", handleCut); // Disabled: https://github.com/ether/etherpad-lite/issues/2546 + // Disabling this can break OL numbering: https://github.com/ether/etherpad-lite/pull/2533 $(root).on("blur", handleBlur); if (browser.msie) { From a67664055d2b687815cb784e1eeaaac12a9fbfd8 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 24 Mar 2015 09:58:02 +0000 Subject: [PATCH 386/455] disable cut renumbering of OLs so cut paste works --- src/static/js/ace2_inner.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 4305e9b48..be54b0c07 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -4863,8 +4863,11 @@ function Ace2Inner(){ $(document).on("keypress", handleKeyEvent); $(document).on("keyup", handleKeyEvent); $(document).on("click", handleClick); - // $(document).on("cut", handleCut); // Disabled: https://github.com/ether/etherpad-lite/issues/2546 - // Disabling this can break OL numbering: https://github.com/ether/etherpad-lite/pull/2533 + + // Disabled: https://github.com/ether/etherpad-lite/issues/2546 + // Will break OL re-numbering: https://github.com/ether/etherpad-lite/pull/2533 + // $(document).on("cut", handleCut); + $(root).on("blur", handleBlur); if (browser.msie) { From ed3ec96838eb35105e847c4691ddf57893ae7e97 Mon Sep 17 00:00:00 2001 From: cristo-rabani Date: Tue, 24 Mar 2015 20:04:28 +0100 Subject: [PATCH 387/455] own list --- src/static/js/AttributeManager.js | 54 +++++++++++++++++-------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/src/static/js/AttributeManager.js b/src/static/js/AttributeManager.js index 5e7823070..af81bb416 100644 --- a/src/static/js/AttributeManager.js +++ b/src/static/js/AttributeManager.js @@ -159,33 +159,37 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ * @param attributeName the name of the attribute to remove, e.g. list * @param attributeValue if given only attributes with equal value will be removed */ - removeAttributeOnLine: function(lineNum, attributeName, attributeValue){ - var builder = Changeset.builder(this.rep.lines.totalWidth()); - var hasMarker = this.lineHasMarker(lineNum); - var found = false; + removeAttributeOnLine: function(lineNum, attributeName, attributeValue){ + var builder = Changeset.builder(this.rep.lines.totalWidth()); + var hasMarker = this.lineHasMarker(lineNum); + var found = false; - var attribs = _(this.getAttributesOnLine(lineNum)).map(function (attrib) { - if (attrib[0] === attributeName && (!attributeValue || attrib[0] === attributeValue)){ - found = true; - return [attributeName, '']; - } - return attrib; - }); + var attribs = _(this.getAttributesOnLine(lineNum)).map(function (attrib) { + if (attrib[0] === attributeName && (!attributeValue || attrib[0] === attributeValue)){ + found = true; + return [attributeName, '']; + } + return attrib; + }); - if (!found) { - return; - } - ChangesetUtils.buildKeepToStartOfRange(this.rep, builder, [lineNum, 0]); - var list = _.chain(attribs).filter(function(a){return !!a[1];}).map(function(a){return a[0];}).value(); - //if we have marker and any of attributes don't need to have marker. we need delete it - if(hasMarker && !_.intersection(lineAttributes,list)){ - ChangesetUtils.buildRemoveRange(this.rep, builder, [lineNum, 1], [lineNum, 2]); - }else{ - ChangesetUtils.buildKeepRange(this.rep, builder, [lineNum, 1], [lineNum, 2], attribs, this.rep.apool); - } - - return this.applyChangeset(builder); - }, + if (!found) { + return; + } + + ChangesetUtils.buildKeepToStartOfRange(this.rep, builder, [lineNum, 0]); + + var countAttribsWithMarker = _.chain(attribs).filter(function(a){return !!a[1];}) + .map(function(a){return a[0];}).difference(['author', 'lmkr', 'insertorder', 'start']).size().value(); + + //if we have marker and any of attributes don't need to have marker. we need delete it + if(hasMarker && !countAttribsWithMarker){ + ChangesetUtils.buildRemoveRange(this.rep, builder, [lineNum, 0], [lineNum, 1]); + }else{ + ChangesetUtils.buildKeepRange(this.rep, builder, [lineNum, 0], [lineNum, 1], attribs, this.rep.apool); + } + + return this.applyChangeset(builder); + }, /* Sets a specified attribute on a line From 5761e998de575f6eac6d332c4def443a5aae977a Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 25 Mar 2015 11:03:45 +0000 Subject: [PATCH 388/455] first semi working alt f9 functionality --- src/node/utils/toolbar.js | 2 +- src/static/css/pad.css | 13 ++++++++----- src/static/js/ace2_inner.js | 14 ++++++++++++++ src/static/js/pad_editbar.js | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 57 insertions(+), 6 deletions(-) diff --git a/src/node/utils/toolbar.js b/src/node/utils/toolbar.js index a5d30f960..65c2c1d54 100644 --- a/src/node/utils/toolbar.js +++ b/src/node/utils/toolbar.js @@ -99,7 +99,7 @@ _.extend(Button.prototype, { }; return tag("li", liAttributes, tag("a", { "class": this.grouping, "data-l10n-id": this.attributes.localizationId }, - tag("span", { "class": " "+ this.attributes.class }) + tag("button", { "class": " "+ this.attributes.class }) ) ); } diff --git a/src/static/css/pad.css b/src/static/css/pad.css index c9ebff4a5..7a91c0b5f 100644 --- a/src/static/css/pad.css +++ b/src/static/css/pad.css @@ -70,10 +70,6 @@ a img { .toolbar ul li { float: left; margin-left: 2px; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; height:32px; } .toolbar ul li.separator { @@ -197,6 +193,7 @@ li[data-key=showusers] > a #online_count { #editbar{ display:none; } + #editorcontainer { position: absolute; top: 37px; /* + 1px border */ @@ -742,13 +739,19 @@ table#otheruserstable { height: 16px; display: inline-block; vertical-align: middle; - + border: none; + padding: 0; + background: none; font-family: "fontawesome-etherpad"; font-size: 15px; font-style: normal; font-weight: normal; color: #666; } + +.buttonicon:focus{ + border: 1px solid #fff; +} .buttonicon-bold:before { content: "\e81c"; } diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index be54b0c07..8e4dab5ca 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -3707,6 +3707,19 @@ function Ace2Inner(){ evt:evt }); specialHandled = (specialHandledInHook&&specialHandledInHook.length>0)?specialHandledInHook[0]:specialHandled; + if ((!specialHandled) && isTypeForSpecialKey && keyCode == 120){ + // Alt F9 focuses on the File Menu and/or editbar. + // Note that while most editors use Alt F10 this is not desirable + // As ubuntu cannot use Alt F10.... + evt.preventDefault(); + // Focus on the editbar. + top.console.log("focusing on first child in menu"); + var firstEditbarElement = parent.parent.$('#editbar').children("ul").first().children().first().children().first().children().first(); + firstEditbarElement.focus(); + top.console.log(firstEditbarElement); + top.console.log(parent.parent.$(':focus')); + $(this).blur(); + } if ((!specialHandled) && isTypeForSpecialKey && keyCode == 8) { // "delete" key; in mozilla, if we're at the beginning of a line, normalize now, @@ -4951,6 +4964,7 @@ function Ace2Inner(){ // a fix: in IE, clicking on a control like a button outside the // iframe can "blur" the editor, causing it to stop getting // events, though typing still affects it(!). + top.console.log("blur handled"); setSelection(null); } } diff --git a/src/static/js/pad_editbar.js b/src/static/js/pad_editbar.js index 7d0539af9..b2d5ada53 100644 --- a/src/static/js/pad_editbar.js +++ b/src/static/js/pad_editbar.js @@ -155,6 +155,10 @@ var padeditbar = (function() }); }); + $('#editbar').on("keyup", function(evt){ + editbarKeyEvent(evt); + }); + $('#editbar').show(); this.redrawHeight(); @@ -300,6 +304,36 @@ var padeditbar = (function() } }; + function editbarKeyEvent(evt){ + // On arrow keys go to next/previous button item in editbar + if(evt.keyCode !== 39 && evt.keyCode !== 37) return; + + // Get our current Focus (Which editbar icon we're currently on) + var currentFocus = $(':focus'); + + // On left arrow move to next button in editbar + if(evt.keyCode === 37){ + var nextFocus = $(currentFocus).parent().parent().prev(); + // No button in this focus so move on + if(nextFocus.find("button").length === 0){ + $(nextFocus).prev().find("button").focus(); + }else{ + $(currentFocus).parent().parent().prev().find("button").focus(); + } + } + + // On right arrow move to next button in editbar + if(evt.keyCode === 39){ + var nextFocus = $(currentFocus).parent().parent().next(); + // No button in this focus so move on + if(nextFocus.find("button").length === 0){ + $(nextFocus).next().find("button").focus(); + }else{ + $(currentFocus).parent().parent().next().find("button").focus(); + } + } + } + function aceAttributeCommand(cmd, ace) { ace.ace_toggleAttributeOnSelection(cmd); } From e8d85c1173353693934cf0fdc396b7f0432f9504 Mon Sep 17 00:00:00 2001 From: Thomas Muehlichen Date: Wed, 25 Mar 2015 12:04:10 +0100 Subject: [PATCH 389/455] feature #2558 added functions to get all attributes at the current or an abritrary position --- src/static/js/AttributeManager.js | 52 ++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/src/static/js/AttributeManager.js b/src/static/js/AttributeManager.js index 865569c53..d2ec324b4 100644 --- a/src/static/js/AttributeManager.js +++ b/src/static/js/AttributeManager.js @@ -98,7 +98,7 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ /* Gets all attributes on a line - @param lineNum: the number of the line to set the attribute for + @param lineNum: the number of the line to get the attribute for */ getAttributesOnLine: function(lineNum){ // get attributes of first char of line @@ -122,6 +122,56 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ return []; }, + /* + Gets all attributes at a position containing line number and column + @param lineNumber starting with zero + @param column starting with zero + returns a list of attributes in the format + [ ["key","value"], ["key","value"], ... ] + */ + getAttributesOnPosition: function(lineNumber, column){ + // get all attributes of the line + var aline = this.rep.alines[lineNumber]; + + if (!aline) { + return []; + } + // iterate through all operations of a line + var opIter = Changeset.opIterator(aline); + + // we need to sum up how much characters each operations take until the wanted position + var currentPointer = 0; + var attributes = []; + var currentOperation; + + while (opIter.hasNext()) { + currentOperation = opIter.next(); + currentPointer = currentPointer + currentOperation.chars; + + if (currentPointer > column) { + // we got the operation of the wanted position, now collect all its attributes + Changeset.eachAttribNumber(currentOperation.attribs, function (n) { + attributes.push([ + this.rep.apool.getAttribKey(n), + this.rep.apool.getAttribValue(n) + ]); + }.bind(this)); + + // skip the loop + return attributes; + } + } + return attributes; + + }, + + /* + Gets all attributes at caret position + */ + getAttributesOnCaret: function(){ + return this.getAttributesOnPosition(this.rep.selStart[0], this.rep.selStart[1]); + }, + /* Sets a specified attribute on a line @param lineNum: the number of the line to set the attribute for From b72127c3277a2992a5b7db5aa7858f4a4dd6cba8 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 25 Mar 2015 12:24:20 +0000 Subject: [PATCH 390/455] different font families for people with dyslexia --- src/locales/en.json | 16 ++++++++ src/static/js/pad.js | 16 ++++++-- src/static/js/pad_editor.js | 76 ++++++++++++++++++++++++++++++------- src/templates/pad.html | 15 ++++++++ 4 files changed, 106 insertions(+), 17 deletions(-) diff --git a/src/locales/en.json b/src/locales/en.json index 23bb3a040..b5713660b 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -39,6 +39,22 @@ "pad.settings.fontType": "Font type:", "pad.settings.fontType.normal": "Normal", "pad.settings.fontType.monospaced": "Monospace", + "pad.settings.fontType.comicsans": "Comic Sans", + "pad.settings.fontType.couriernew": "Courier New", + "pad.settings.fontType.georgia": "Georgia", + "pad.settings.fontType.impact": "Impact", + "pad.settings.fontType.lucida": "Lucida", + "pad.settings.fontType.lucidasans": "Lucida Sans", + "pad.settings.fontType.palatino": "Palatino", + "pad.settings.fontType.tahoma": "Tahoma", + "pad.settings.fontType.timesnewroman": "Times New Roman", + "pad.settings.fontType.trebuchet": "Trebuchet", + "pad.settings.fontType.verdana": "Verdana", + "pad.settings.fontType.symbol": "Symbol", + "pad.settings.fontType.webdings": "Webdings", + "pad.settings.fontType.wingdings": "Wingdings", + "pad.settings.fontType.sansserif": "Sans Serif", + "pad.settings.fontType.serif": "Serif", "pad.settings.globalView": "Global View", "pad.settings.language": "Language:", diff --git a/src/static/js/pad.js b/src/static/js/pad.js index 77bfab7f1..0e54168b0 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.js @@ -576,9 +576,18 @@ var pad = { if(padcookie.getPref("rtlIsTrue") == true){ pad.changeViewOption('rtlIsTrue', true); } - if(padcookie.getPref("useMonospaceFont") == true){ - pad.changeViewOption('useMonospaceFont', true); - } + + var fonts = ['useMonospaceFont', 'useComicSansFont', 'useCourierNewFont', 'useGeorgiaFont', 'useImpactFont', + 'useLucidaFont', 'useLucidaSansFont', 'usePalatinoFont', 'useTahomaFont', 'useTimesNewRomanFont', + 'useTrebuchetFont', 'useVerdanaFont', 'useSymbolFont', 'useWebdingsFont', 'useWingDingsFont', 'useSansSerifFont', + 'useSerifFont']; + + $.each(fonts, function(i, font){ + if(padcookie.getPref(font) == true){ + pad.changeViewOption(font, true); + } + }) + hooks.aCallAll("postAceInit", {ace: padeditor.ace, pad: pad}); } }, @@ -629,6 +638,7 @@ var pad = { for (var k in opts.view) { pad.padOptions.view[k] = opts.view[k]; +console.log("setpref", k, opts.view[k]); padcookie.setPref(k, opts.view[k]); } padeditor.setViewOptions(pad.padOptions.view); diff --git a/src/static/js/pad_editor.js b/src/static/js/pad_editor.js index b73409ff3..51b9355e5 100644 --- a/src/static/js/pad_editor.js +++ b/src/static/js/pad_editor.js @@ -28,6 +28,13 @@ var padeditor = (function() var Ace2Editor = undefined; var pad = undefined; var settings = undefined; + + // Array of available fonts + var fonts = ['useMonospaceFont', 'useComicSansFont', 'useCourierNewFont', 'useGeorgiaFont', 'useImpactFont', + 'useLucidaFont', 'useLucidaSansFont', 'usePalatinoFont', 'useTahomaFont', 'useTimesNewRomanFont', + 'useTrebuchetFont', 'useVerdanaFont', 'useSymbolFont', 'useWebdingsFont', 'useWingDingsFont', 'useSansSerifFont', + 'useSerifFont']; + var self = { ace: null, // this is accessed directly from other files @@ -85,10 +92,15 @@ var padeditor = (function() padutils.setCheckbox($("#options-rtlcheck"), ('rtl' == html10n.getDirection())); }) - // font face + // font family change $("#viewfontmenu").change(function() { - pad.changeViewOption('useMonospaceFont', $("#viewfontmenu").val() == 'monospace'); + $.each(fonts, function(i, font){ + var sfont = font.replace("use",""); + sfont = sfont.replace("Font",""); + sfont = sfont.toLowerCase(); + pad.changeViewOption(font, $("#viewfontmenu").val() == sfont); + }); }); // Language @@ -98,12 +110,12 @@ var padeditor = (function() // this does not interfere with html10n's normal value-setting because html10n just ingores s // also, a value which has been set by the user will be not overwritten since a user-edited // does *not* have the editempty-class - $('input[data-l10n-id]').each(function(key, input) - { - input = $(input); - if(input.hasClass("editempty")) - input.val(html10n.get(input.attr("data-l10n-id"))); - }); + $('input[data-l10n-id]').each(function(key, input){ + input = $(input); + if(input.hasClass("editempty")){ + input.val(html10n.get(input.attr("data-l10n-id"))); + } + }); }) $("#languagemenu").val(html10n.getLanguage()); $("#languagemenu").change(function() { @@ -136,13 +148,49 @@ var padeditor = (function() v = getOption('showAuthorColors', true); self.ace.setProperty("showsauthorcolors", v); padutils.setCheckbox($("#options-colorscheck"), v); - // Override from parameters if true - if (settings.noColors !== false) - self.ace.setProperty("showsauthorcolors", !settings.noColors); - v = getOption('useMonospaceFont', false); - self.ace.setProperty("textface", (v ? "monospace" : "Arial, sans-serif")); - $("#viewfontmenu").val(v ? "monospace" : "normal"); + // Override from parameters if true + if (settings.noColors !== false){ + self.ace.setProperty("showsauthorcolors", !settings.noColors); + } + + var normalFont = true; + // Go through each font and see if the option is set.. + $.each(fonts, function(i, font){ + var isEnabled = getOption(font, false); + if(isEnabled){ + font = font.replace("use",""); + font = font.replace("Font",""); + font = font.toLowerCase(); + if(font === "monospace") self.ace.setProperty("textface", "Courier new"); + if(font === "comicsans") self.ace.setProperty("textface", "Comic Sans MS"); + if(font === "georgia") self.ace.setProperty("textface", "Georgia"); + if(font === "impact") self.ace.setProperty("textface", "Impact"); + if(font === "lucida") self.ace.setProperty("textface", "Lucida"); + if(font === "lucidasans") self.ace.setProperty("textface", "Lucida Sans Unicode"); + if(font === "palatino") self.ace.setProperty("textface", "Palatino Linotype"); + if(font === "tahoma") self.ace.setProperty("textface", "Tahoma"); + if(font === "timesnewroman") self.ace.setProperty("textface", "Times New Roman"); + if(font === "trebuchet") self.ace.setProperty("textface", "Trebuchet MS"); + if(font === "verdana") self.ace.setProperty("textface", "Verdana"); + if(font === "symbol") self.ace.setProperty("textface", "Symbol"); + if(font === "webdings") self.ace.setProperty("textface", "Webdings"); + if(font === "wingdings") self.ace.setProperty("textface", "Wingdings"); + if(font === "sansserif") self.ace.setProperty("textface", "MS Sans Serif"); + if(font === "serif") self.ace.setProperty("textface", "MS Serif"); + + // $("#viewfontmenu").val(font); + normalFont = false; + } + }); + + // No font has been previously selected so use the Normal font + if(normalFont){ + console.log("Enabling a normal font"); + self.ace.setProperty("textface", "Arial, sans-serif"); + // $("#viewfontmenu").val("normal"); + } + }, dispose: function() { diff --git a/src/templates/pad.html b/src/templates/pad.html index 7c7257cc6..99997615c 100644 --- a/src/templates/pad.html +++ b/src/templates/pad.html @@ -160,6 +160,21 @@ From be73e729d4571e9541cbf703d1737fdc0db62fd7 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 25 Mar 2015 12:25:33 +0000 Subject: [PATCH 391/455] remove console log --- src/static/js/pad.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/static/js/pad.js b/src/static/js/pad.js index 0e54168b0..71fae6784 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.js @@ -638,7 +638,6 @@ var pad = { for (var k in opts.view) { pad.padOptions.view[k] = opts.view[k]; -console.log("setpref", k, opts.view[k]); padcookie.setPref(k, opts.view[k]); } padeditor.setViewOptions(pad.padOptions.view); From c6cac53ddada4eb89bb4b74dc4a7be9131b30d1d Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 25 Mar 2015 12:25:49 +0000 Subject: [PATCH 392/455] remove console log --- src/static/js/pad_editor.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/static/js/pad_editor.js b/src/static/js/pad_editor.js index 51b9355e5..ca7dc2b0b 100644 --- a/src/static/js/pad_editor.js +++ b/src/static/js/pad_editor.js @@ -186,7 +186,6 @@ var padeditor = (function() // No font has been previously selected so use the Normal font if(normalFont){ - console.log("Enabling a normal font"); self.ace.setProperty("textface", "Arial, sans-serif"); // $("#viewfontmenu").val("normal"); } From fbcbc3c8a2ffef1628ac13a80c21573e61455723 Mon Sep 17 00:00:00 2001 From: Thomas Muehlichen Date: Wed, 25 Mar 2015 13:29:03 +0100 Subject: [PATCH 393/455] feature #2558 more precise documentation --- src/static/js/AttributeManager.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/static/js/AttributeManager.js b/src/static/js/AttributeManager.js index d2ec324b4..b822caee0 100644 --- a/src/static/js/AttributeManager.js +++ b/src/static/js/AttributeManager.js @@ -166,7 +166,8 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ }, /* - Gets all attributes at caret position + Gets all attributes at caret position + if the user selected a range, the start of the selection is taken */ getAttributesOnCaret: function(){ return this.getAttributesOnPosition(this.rep.selStart[0], this.rep.selStart[1]); From 0c902ced7399ad1aacde688b34096c8851a6d699 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 25 Mar 2015 15:19:52 +0000 Subject: [PATCH 394/455] better logic for handling lr arrows --- src/static/js/pad_editbar.js | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/src/static/js/pad_editbar.js b/src/static/js/pad_editbar.js index b2d5ada53..14e33fe14 100644 --- a/src/static/js/pad_editbar.js +++ b/src/static/js/pad_editbar.js @@ -304,34 +304,27 @@ var padeditbar = (function() } }; + var editbarPosition = 0; + function editbarKeyEvent(evt){ // On arrow keys go to next/previous button item in editbar if(evt.keyCode !== 39 && evt.keyCode !== 37) return; - // Get our current Focus (Which editbar icon we're currently on) - var currentFocus = $(':focus'); + // Get all the focusable items in the editbar + var focusItems = $('#editbar').find('button, select'); // On left arrow move to next button in editbar if(evt.keyCode === 37){ - var nextFocus = $(currentFocus).parent().parent().prev(); - // No button in this focus so move on - if(nextFocus.find("button").length === 0){ - $(nextFocus).prev().find("button").focus(); - }else{ - $(currentFocus).parent().parent().prev().find("button").focus(); - } + editbarPosition--; + $(focusItems[editbarPosition]).focus(); } // On right arrow move to next button in editbar if(evt.keyCode === 39){ - var nextFocus = $(currentFocus).parent().parent().next(); - // No button in this focus so move on - if(nextFocus.find("button").length === 0){ - $(nextFocus).next().find("button").focus(); - }else{ - $(currentFocus).parent().parent().next().find("button").focus(); - } + editbarPosition++; + $(focusItems[editbarPosition]).focus(); } + } function aceAttributeCommand(cmd, ace) { From bc760e9494ab66f8ff72d14ec08614c286ebc5d3 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 25 Mar 2015 15:38:19 +0000 Subject: [PATCH 395/455] logic to send focus back to pad on Alt F9 when focus on editbar --- src/static/js/ace2_inner.js | 5 +---- src/static/js/pad_editbar.js | 23 ++++++++++++++++++++++- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 8e4dab5ca..4c8c5204e 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -3712,12 +3712,9 @@ function Ace2Inner(){ // Note that while most editors use Alt F10 this is not desirable // As ubuntu cannot use Alt F10.... evt.preventDefault(); - // Focus on the editbar. - top.console.log("focusing on first child in menu"); + // Focus on the editbar. -- TODO: Move Focus back to previous state (we know it so we can use it) var firstEditbarElement = parent.parent.$('#editbar').children("ul").first().children().first().children().first().children().first(); firstEditbarElement.focus(); - top.console.log(firstEditbarElement); - top.console.log(parent.parent.$(':focus')); $(this).blur(); } if ((!specialHandled) && isTypeForSpecialKey && keyCode == 8) diff --git a/src/static/js/pad_editbar.js b/src/static/js/pad_editbar.js index 14e33fe14..92e671c4d 100644 --- a/src/static/js/pad_editbar.js +++ b/src/static/js/pad_editbar.js @@ -155,7 +155,7 @@ var padeditbar = (function() }); }); - $('#editbar').on("keyup", function(evt){ + $('#editbar').on("keydown", function(evt){ editbarKeyEvent(evt); }); @@ -307,6 +307,13 @@ var padeditbar = (function() var editbarPosition = 0; function editbarKeyEvent(evt){ + // If the event is Alt F9 (we're already in the editbar menu + // Send the users focus back to the pad + if(evt.keyCode === 120){ + // If we're in the editbar already.. + padeditor.ace.focus(); // Sends focus back + } + // On arrow keys go to next/previous button item in editbar if(evt.keyCode !== 39 && evt.keyCode !== 37) return; @@ -338,6 +345,20 @@ var padeditbar = (function() toolbar.registerDropdownCommand("import_export"); toolbar.registerDropdownCommand("embed"); + toolbar.registerCommand("import_export", function () { + setTimeout(function(){ + $('#importfileinput').focus(); + },100); + toolbar.toggleDropDown("import_export"); + }); + + toolbar.registerCommand("showusers", function () { + setTimeout(function(){ + $('#myusernameedit').focus(); // TODO: Not working + },100); + toolbar.toggleDropDown("users"); + }); + toolbar.registerCommand("embed", function () { toolbar.setEmbedLinks(); $('#linkinput').focus().select(); From e9360b6ed2b96e03c68a207e7562a87949e4b87a Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 25 Mar 2015 15:49:41 +0000 Subject: [PATCH 396/455] working f9 logic --- src/static/js/ace2_inner.js | 5 +++-- src/static/js/lodash.js | 1 + src/static/js/pad_editbar.js | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 src/static/js/lodash.js diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 4c8c5204e..2b7e2d4ad 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -3711,11 +3711,12 @@ function Ace2Inner(){ // Alt F9 focuses on the File Menu and/or editbar. // Note that while most editors use Alt F10 this is not desirable // As ubuntu cannot use Alt F10.... - evt.preventDefault(); // Focus on the editbar. -- TODO: Move Focus back to previous state (we know it so we can use it) var firstEditbarElement = parent.parent.$('#editbar').children("ul").first().children().first().children().first().children().first(); - firstEditbarElement.focus(); + // top.console.log("fEE", firstEditbarElement); $(this).blur(); + firstEditbarElement.focus(); + evt.preventDefault(); } if ((!specialHandled) && isTypeForSpecialKey && keyCode == 8) { diff --git a/src/static/js/lodash.js b/src/static/js/lodash.js new file mode 100644 index 000000000..349f95791 --- /dev/null +++ b/src/static/js/lodash.js @@ -0,0 +1 @@ +module.exports = require('lodash'); diff --git a/src/static/js/pad_editbar.js b/src/static/js/pad_editbar.js index 92e671c4d..70360924c 100644 --- a/src/static/js/pad_editbar.js +++ b/src/static/js/pad_editbar.js @@ -63,6 +63,7 @@ ToolbarItem.prototype.bind = function (callback) { if (self.isButton()) { self.$el.click(function (event) { + $(':focus').blur(); callback(self.getCommand(), self); event.preventDefault(); }); @@ -311,6 +312,7 @@ var padeditbar = (function() // Send the users focus back to the pad if(evt.keyCode === 120){ // If we're in the editbar already.. + $(':focus').blur(); // required to do not try to remove! padeditor.ace.focus(); // Sends focus back } From aff802a0b3e4f050a0558db5c91d24884751d516 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 25 Mar 2015 21:15:44 +0000 Subject: [PATCH 397/455] whoops --- src/static/js/lodash.js | 1 - 1 file changed, 1 deletion(-) delete mode 100644 src/static/js/lodash.js diff --git a/src/static/js/lodash.js b/src/static/js/lodash.js deleted file mode 100644 index 349f95791..000000000 --- a/src/static/js/lodash.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('lodash'); From e67ae522e22afedac8585209eabdd3f388a24c78 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 25 Mar 2015 23:30:17 +0000 Subject: [PATCH 398/455] support drop down show events --- src/static/js/ace2_inner.js | 1 - src/static/js/pad_editbar.js | 11 ++++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 2b7e2d4ad..e190bfd71 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -4962,7 +4962,6 @@ function Ace2Inner(){ // a fix: in IE, clicking on a control like a button outside the // iframe can "blur" the editor, causing it to stop getting // events, though typing still affects it(!). - top.console.log("blur handled"); setSelection(null); } } diff --git a/src/static/js/pad_editbar.js b/src/static/js/pad_editbar.js index 70360924c..022e94934 100644 --- a/src/static/js/pad_editbar.js +++ b/src/static/js/pad_editbar.js @@ -347,6 +347,13 @@ var padeditbar = (function() toolbar.registerDropdownCommand("import_export"); toolbar.registerDropdownCommand("embed"); + toolbar.registerCommand("settings", function () { + setTimeout(function(){ + $('#options-stickychat').focus(); + },100); + toolbar.toggleDropDown("settings"); + }); + toolbar.registerCommand("import_export", function () { setTimeout(function(){ $('#importfileinput').focus(); @@ -363,7 +370,9 @@ var padeditbar = (function() toolbar.registerCommand("embed", function () { toolbar.setEmbedLinks(); - $('#linkinput').focus().select(); + setTimeout(function(){ + $('#linkinput').focus().select(); + }, 100); toolbar.toggleDropDown("embed"); }); From 69f0392e75534e9340f6349c5af3a765454ec78a Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 26 Mar 2015 11:24:06 +0000 Subject: [PATCH 399/455] escape to exit editbar --- 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 022e94934..a16cc63fa 100644 --- a/src/static/js/pad_editbar.js +++ b/src/static/js/pad_editbar.js @@ -310,7 +310,7 @@ var padeditbar = (function() function editbarKeyEvent(evt){ // If the event is Alt F9 (we're already in the editbar menu // Send the users focus back to the pad - if(evt.keyCode === 120){ + if(evt.keyCode === 120 || evt.keyCode === 27){ // If we're in the editbar already.. $(':focus').blur(); // required to do not try to remove! padeditor.ace.focus(); // Sends focus back From 4362a42f7f0fa2919e26fd7806181bf9bb563cc5 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 26 Mar 2015 11:38:55 +0000 Subject: [PATCH 400/455] make index page accessible for screen readers --- src/templates/index.html | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/templates/index.html b/src/templates/index.html index 02ecf67a0..626630e3e 100644 --- a/src/templates/index.html +++ b/src/templates/index.html @@ -70,9 +70,10 @@ } #button { margin: 0 auto; - border-radius: 3px; text-align: center; font: 36px verdana,arial,sans-serif; + width:300px; + border:none; color: white; text-shadow: 0 -1px 0 rgba(0,0,0,.8); height: 70px; @@ -100,6 +101,7 @@ text-align: left; text-shadow: 0 1px 1px #fff; margin: 16px auto 0; + display:block; } #padname{ height:38px; @@ -158,8 +160,8 @@
      <% e.begin_block("indexWrapper"); %>
      -
      -
      + +
      From ec1956b4b6a41b92e636297cf5a9e8cf25938f79 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 26 Mar 2015 12:42:07 +0000 Subject: [PATCH 401/455] read for button activity --- src/node/utils/toolbar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node/utils/toolbar.js b/src/node/utils/toolbar.js index 65c2c1d54..85af619c4 100644 --- a/src/node/utils/toolbar.js +++ b/src/node/utils/toolbar.js @@ -99,7 +99,7 @@ _.extend(Button.prototype, { }; return tag("li", liAttributes, tag("a", { "class": this.grouping, "data-l10n-id": this.attributes.localizationId }, - tag("button", { "class": " "+ this.attributes.class }) + tag("button", { "class": " "+ this.attributes.class, "title": this.attributes.command }) ) ); } From 1a5ea8707fdb2c692f5d69980a057b1c09f81ab7 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 26 Mar 2015 13:16:54 +0000 Subject: [PATCH 402/455] make the pad contents act like an application and not contents, this can probably be togglable but default state should be editable --- src/static/js/ace.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/static/js/ace.js b/src/static/js/ace.js index addc412fc..376e786b1 100644 --- a/src/static/js/ace.js +++ b/src/static/js/ace.js @@ -265,7 +265,7 @@ plugins.ensure(function () {\n\ iframeHTML: iframeHTML }); - iframeHTML.push(' '); + iframeHTML.push(' '); // Expose myself to global for my child frame. var thisFunctionsName = "ChildAccessibleAce2Editor"; From 959aa92656365a69d1502c13fa9a0c490c18cd06 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 26 Mar 2015 13:32:45 +0000 Subject: [PATCH 403/455] stop listing URL each time focus is placed back on inner iframe --- src/static/js/ace.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/static/js/ace.js b/src/static/js/ace.js index 376e786b1..a9bbb0d9a 100644 --- a/src/static/js/ace.js +++ b/src/static/js/ace.js @@ -279,6 +279,7 @@ window.onload = function () {\n\ setTimeout(function () {\n\ var iframe = document.createElement("IFRAME");\n\ iframe.name = "ace_inner";\n\ + iframe.title = "pad";\n\ iframe.scrolling = "no";\n\ var outerdocbody = document.getElementById("outerdocbody");\n\ iframe.frameBorder = 0;\n\ From 7b726cbc02e21bec9a267f0ba6fd8ab31a2dbded Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 26 Mar 2015 13:58:57 +0000 Subject: [PATCH 404/455] more working logic and also pretty nice screen reader experience --- src/node/utils/toolbar.js | 2 ++ src/static/js/ace.js | 1 + src/static/js/pad_editbar.js | 2 +- src/templates/pad.html | 4 ++-- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/node/utils/toolbar.js b/src/node/utils/toolbar.js index 85af619c4..8905eee1d 100644 --- a/src/node/utils/toolbar.js +++ b/src/node/utils/toolbar.js @@ -105,6 +105,8 @@ _.extend(Button.prototype, { } }); + + SelectButton = function (attributes) { this.attributes = attributes; this.options = []; diff --git a/src/static/js/ace.js b/src/static/js/ace.js index a9bbb0d9a..c446939a3 100644 --- a/src/static/js/ace.js +++ b/src/static/js/ace.js @@ -320,6 +320,7 @@ window.onload = function () {\n\ var outerFrame = document.createElement("IFRAME"); outerFrame.name = "ace_outer"; outerFrame.frameBorder = 0; // for IE + outerFrame.title = "Ether"; info.frame = outerFrame; document.getElementById(containerId).appendChild(outerFrame); diff --git a/src/static/js/pad_editbar.js b/src/static/js/pad_editbar.js index a16cc63fa..c71d2b8ae 100644 --- a/src/static/js/pad_editbar.js +++ b/src/static/js/pad_editbar.js @@ -325,7 +325,7 @@ var padeditbar = (function() // On left arrow move to next button in editbar if(evt.keyCode === 37){ editbarPosition--; - $(focusItems[editbarPosition]).focus(); + $(focusItems[editbarPosition]).focus() } // On right arrow move to next button in editbar diff --git a/src/templates/pad.html b/src/templates/pad.html index 99997615c..c18a67d9a 100644 --- a/src/templates/pad.html +++ b/src/templates/pad.html @@ -61,12 +61,12 @@
      -
      -
      +
      From 19e83d54059df5a653d7f4f7fc9cd60bf012b1fb Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 26 Mar 2015 16:58:13 +0000 Subject: [PATCH 408/455] much better chat focus toggle behavior --- src/static/js/ace2_inner.js | 5 +++-- src/static/js/chat.js | 19 +++++++++++++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index cb09432be..5442dd080 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -3636,6 +3636,7 @@ function Ace2Inner(){ var charCode = evt.charCode; var keyCode = evt.keyCode; var which = evt.which; + var altKey = evt.altKey; // prevent ESC key if (keyCode == 27) @@ -3718,11 +3719,11 @@ function Ace2Inner(){ firstEditbarElement.focus(); evt.preventDefault(); } - if ((!specialHandled) && isTypeForSpecialKey && keyCode == 67){ + if ((!specialHandled) && altKey && keyCode == 67){ // Alt c focuses on the Chat window + $(this).blur(); parent.parent.chat.show(); parent.parent.chat.focus(); - $(this).blur(); evt.preventDefault(); } if ((!specialHandled) && isTypeForSpecialKey && keyCode == 8) diff --git a/src/static/js/chat.js b/src/static/js/chat.js index 021a906aa..7edf73bab 100644 --- a/src/static/js/chat.js +++ b/src/static/js/chat.js @@ -18,6 +18,7 @@ var padutils = require('./pad_utils').padutils; var padcookie = require('./pad_cookie').padcookie; var Tinycon = require('tinycon/tinycon'); var hooks = require('./pluginfw/hooks'); +var padeditor = require('./pad_editor').padeditor; var chat = (function() { @@ -38,7 +39,11 @@ var chat = (function() }, focus: function () { - $("#chatinput").focus(); + // I'm not sure why we need a setTimeout here but without it we don't get focus... + // Animation maybe? + setTimeout(function(){ + $("#chatinput").focus(); + },100); }, stickToScreen: function(fromInitialCall) // Make chat stick to right hand side of screen { @@ -209,8 +214,18 @@ var chat = (function() init: function(pad) { this._pad = pad; - $("#chatinput").keypress(function(evt) + $("#chatinput").keyup(function(evt) { + // If the event is Alt C or Escape & we're already in the chat menu + // Send the users focus back to the pad + if((evt.altKey == true && evt.which === 67) || evt.which === 27){ + // If we're in chat already.. + $(':focus').blur(); // required to do not try to remove! + padeditor.ace.focus(); // Sends focus back to pad + } + }); + + $("#chatinput").keypress(function(evt){ //if the user typed enter, fire the send if(evt.which == 13 || evt.which == 10) { From 1c05933dc91c9669a2f8407ab471c204f6975e98 Mon Sep 17 00:00:00 2001 From: Thomas Muehlichen Date: Thu, 26 Mar 2015 18:49:35 +0100 Subject: [PATCH 409/455] Feature #2567 Added workaround to enable contentcollector to write key-value attributes --- src/static/js/contentcollector.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index e428c63f9..cb3cf7b0b 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -297,7 +297,23 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas { if (state.attribs[a]) { - lst.push([a, 'true']); + // The following splitting of the attribute name is a workaround + // to enable the content collector to store key-value attributes + // see https://github.com/ether/etherpad-lite/issues/2567 for more information + // in long term the contentcollector should be refactored to get rid of this workaround + var ATTRIBUTE_SPLIT_STRING = "::"; + + // see if attributeString is splittable + var attributeSplits = a.split(ATTRIBUTE_SPLIT_STRING); + if (attributeSplits.length > 1) { + // the attribute name follows the convention key::value + // so save it as a key value attribute + lst.push([attributeSplits[0], attributeSplits[1]]); + } else { + // the "normal" case, the attribute is just a switch + // so set it true + lst.push([a, 'true']); + } } } if (state.authorLevel > 0) From f9071aebe6deff70e84d38b5508d730edadc4902 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 30 Mar 2015 14:56:35 +0100 Subject: [PATCH 410/455] fix issue with showControls false not working --- src/static/css/pad.css | 6 ++++++ src/static/js/pad.js | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/static/css/pad.css b/src/static/css/pad.css index 1b730c3c0..5b5d7dec0 100644 --- a/src/static/css/pad.css +++ b/src/static/css/pad.css @@ -1262,3 +1262,9 @@ input[type=checkbox] { -moz-osx-font-smoothing: grayscale; } +.hideControlsEditor{ + top:0px !important; +} +.hideControlsEditbar{ + display:none !important; +} diff --git a/src/static/js/pad.js b/src/static/js/pad.js index 71fae6784..0d896a607 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.js @@ -110,7 +110,7 @@ function randomString() // callback: the function to call when all above succeeds, `val` is the value supplied by the user var getParameters = [ { name: "noColors", checkVal: "true", callback: function(val) { settings.noColors = true; $('#clearAuthorship').hide(); } }, - { name: "showControls", checkVal: "false", callback: function(val) { $('#editbar').hide(); $('#editorcontainer').css({"top":"0px"}); } }, + { name: "showControls", checkVal: "false", callback: function(val) { $('#editbar').addClass('hideControlsEditbar'); $('#editorcontainer').addClass('hideControlsEditor'); } }, { name: "showChat", checkVal: "false", callback: function(val) { $('#chaticon').hide(); } }, { name: "showLineNumbers", checkVal: "false", callback: function(val) { settings.LineNumbersDisabled = true; } }, { name: "useMonospaceFont", checkVal: "true", callback: function(val) { settings.useMonospaceFontGlobal = true; } }, From 1e8e64d675691b4c8dc0f461958a439ec33d6154 Mon Sep 17 00:00:00 2001 From: Thomas Muehlichen Date: Tue, 31 Mar 2015 10:50:20 +0200 Subject: [PATCH 411/455] feature #2567 added documentation --- doc/api/hooks_client-side.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/api/hooks_client-side.md b/doc/api/hooks_client-side.md index 8e2d3da79..f9ad9147c 100644 --- a/doc/api/hooks_client-side.md +++ b/doc/api/hooks_client-side.md @@ -203,6 +203,13 @@ Things in context: This hook is called before the content of a node is collected by the usual methods. The cc object can be used to do a bunch of things that modify the content of the pad. See, for example, the heading1 plugin for etherpad original. +E.g. if you need to apply an attribute to newly inserted characters, +call cc.doAttrib(state, "attributeName") which results in an attribute attributeName=true. + +If you want to specify also a value, call cc.doAttrib(state, "attributeName:value") +which results in an attribute attributeName=value. + + ## collectContentImage Called from: src/static/js/contentcollector.js From a930161cb9aba6524ffa344863e44e83cc578b6d Mon Sep 17 00:00:00 2001 From: Thomas Muehlichen Date: Tue, 31 Mar 2015 10:58:47 +0200 Subject: [PATCH 412/455] feature #2558 added documentation --- src/static/js/AttributeManager.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/static/js/AttributeManager.js b/src/static/js/AttributeManager.js index b822caee0..1d1c4a913 100644 --- a/src/static/js/AttributeManager.js +++ b/src/static/js/AttributeManager.js @@ -168,6 +168,8 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ /* Gets all attributes at caret position if the user selected a range, the start of the selection is taken + returns a list of attributes in the format + [ ["key","value"], ["key","value"], ... ] */ getAttributesOnCaret: function(){ return this.getAttributesOnPosition(this.rep.selStart[0], this.rep.selStart[1]); From a82e692bdd39feced285b6bd25c3ccb39b63434b Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 31 Mar 2015 13:21:41 +0100 Subject: [PATCH 413/455] dont use nasty settimeouts use a callback instead for focus --- src/static/js/pad_editbar.js | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/static/js/pad_editbar.js b/src/static/js/pad_editbar.js index aa9f183b8..8df6e693e 100644 --- a/src/static/js/pad_editbar.js +++ b/src/static/js/pad_editbar.js @@ -352,32 +352,30 @@ var padeditbar = (function() toolbar.registerDropdownCommand("embed"); toolbar.registerCommand("settings", function () { - setTimeout(function(){ + toolbar.toggleDropDown("settings", function(){ $('#options-stickychat').focus(); - },100); - toolbar.toggleDropDown("settings"); + }); }); toolbar.registerCommand("import_export", function () { - setTimeout(function(){ - $('#importfileinput').focus(); - },100); - toolbar.toggleDropDown("import_export"); + toolbar.toggleDropDown("import_export", function(){ + setTimeout(function(){ + $('#importfileinput').focus(); + }, 100); + }); }); toolbar.registerCommand("showusers", function () { - setTimeout(function(){ - $('#myusernameedit').focus(); // TODO: Not working - },100); - toolbar.toggleDropDown("users"); + toolbar.toggleDropDown("users", function(){ + $('#myusernameedit').focus(); + }); }); toolbar.registerCommand("embed", function () { toolbar.setEmbedLinks(); - setTimeout(function(){ + toolbar.toggleDropDown("embed", function(){ $('#linkinput').focus().select(); - }, 100); - toolbar.toggleDropDown("embed"); + }); }); toolbar.registerCommand("savedRevision", function () { From 35948989b398534cbd2a2817dc8553abc8077c93 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 31 Mar 2015 13:45:11 +0100 Subject: [PATCH 414/455] make Alt C and Alt F9 and Escape work from anywhere --- src/static/js/ace2_inner.js | 1 + src/static/js/chat.js | 10 ++++++++++ src/static/js/pad_editbar.js | 10 ++++++---- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 5442dd080..a5e53eecf 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -4960,6 +4960,7 @@ function Ace2Inner(){ function focus() { + top.console.log("window.focus there is a bug with me in FF"); window.focus(); } diff --git a/src/static/js/chat.js b/src/static/js/chat.js index 7edf73bab..42cd50f4b 100644 --- a/src/static/js/chat.js +++ b/src/static/js/chat.js @@ -225,6 +225,16 @@ var chat = (function() } }); + $('body:not(#chatinput)').on("keydown", function(evt){ + if (evt.altKey && evt.which == 67){ + // Alt c focuses on the Chat window + $(this).blur(); + parent.parent.chat.show(); + parent.parent.chat.focus(); + evt.preventDefault(); + } + }); + $("#chatinput").keypress(function(evt){ //if the user typed enter, fire the send if(evt.which == 13 || evt.which == 10) diff --git a/src/static/js/pad_editbar.js b/src/static/js/pad_editbar.js index 8df6e693e..991c85949 100644 --- a/src/static/js/pad_editbar.js +++ b/src/static/js/pad_editbar.js @@ -156,8 +156,8 @@ var padeditbar = (function() }); }); - $('#editbar, .popup').on("keydown", function(evt){ - editbarKeyEvent(evt); + $('body:not(#editorcontainerbox)').on("keydown", function(evt){ + bodyKeyEvent(evt); }); $('#editbar').show(); @@ -307,10 +307,10 @@ var padeditbar = (function() var editbarPosition = 0; - function editbarKeyEvent(evt){ + function bodyKeyEvent(evt){ // If the event is Alt F9 or Escape & we're already in the editbar menu // Send the users focus back to the pad - if(evt.keyCode === 120 || evt.keyCode === 27){ + if((evt.keyCode === 120 && evt.altKey) || evt.keyCode === 27){ // If we're in the editbar already.. // Close any dropdowns we have open.. padeditbar.toggleDropDown("none"); @@ -318,6 +318,8 @@ var padeditbar = (function() // Shift focus away from any drop downs $(':focus').blur(); // required to do not try to remove! padeditor.ace.focus(); // Sends focus back to pad + // The above focus doesn't always work in FF, you have to hit enter afterwards + // This still needs fixing cake } // On arrow keys go to next/previous button item in editbar From ef38bcad9ff6e929ad0aa87b260ca73154270774 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 31 Mar 2015 13:57:18 +0100 Subject: [PATCH 415/455] make buttons i18n friendly and a better experience --- src/node/utils/toolbar.js | 2 +- src/static/css/pad.css | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/node/utils/toolbar.js b/src/node/utils/toolbar.js index 8905eee1d..5d735323d 100644 --- a/src/node/utils/toolbar.js +++ b/src/node/utils/toolbar.js @@ -99,7 +99,7 @@ _.extend(Button.prototype, { }; return tag("li", liAttributes, tag("a", { "class": this.grouping, "data-l10n-id": this.attributes.localizationId }, - tag("button", { "class": " "+ this.attributes.class, "title": this.attributes.command }) + tag("button", { "class": " "+ this.attributes.class, "alt": this.attributes.command, "data-l10n-id": this.attributes.localizationId }) ) ); } diff --git a/src/static/css/pad.css b/src/static/css/pad.css index 5b5d7dec0..ee0a0a96b 100644 --- a/src/static/css/pad.css +++ b/src/static/css/pad.css @@ -747,6 +747,7 @@ table#otheruserstable { font-style: normal; font-weight: normal; color: #666; + cursor: pointer; } .buttonicon::-moz-focus-inner { From 3126d7196ef97f4a2720a968d4ece405747655e4 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 31 Mar 2015 14:06:02 +0100 Subject: [PATCH 416/455] fix alt and focus issues --- src/static/js/ace2_inner.js | 4 +--- src/static/js/pad_editbar.js | 1 + 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index a5e53eecf..32492e348 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -3708,13 +3708,12 @@ function Ace2Inner(){ evt:evt }); specialHandled = (specialHandledInHook&&specialHandledInHook.length>0)?specialHandledInHook[0]:specialHandled; - if ((!specialHandled) && isTypeForSpecialKey && keyCode == 120){ + if ((!specialHandled) && altKey && isTypeForSpecialKey && keyCode == 120){ // Alt F9 focuses on the File Menu and/or editbar. // Note that while most editors use Alt F10 this is not desirable // As ubuntu cannot use Alt F10.... // Focus on the editbar. -- TODO: Move Focus back to previous state (we know it so we can use it) var firstEditbarElement = parent.parent.$('#editbar').children("ul").first().children().first().children().first().children().first(); - // top.console.log("fEE", firstEditbarElement); $(this).blur(); firstEditbarElement.focus(); evt.preventDefault(); @@ -4960,7 +4959,6 @@ function Ace2Inner(){ function focus() { - top.console.log("window.focus there is a bug with me in FF"); window.focus(); } diff --git a/src/static/js/pad_editbar.js b/src/static/js/pad_editbar.js index 991c85949..75bf40f06 100644 --- a/src/static/js/pad_editbar.js +++ b/src/static/js/pad_editbar.js @@ -320,6 +320,7 @@ var padeditbar = (function() padeditor.ace.focus(); // Sends focus back to pad // The above focus doesn't always work in FF, you have to hit enter afterwards // This still needs fixing cake + evt.preventDefault(); } // On arrow keys go to next/previous button item in editbar From 0f0a6c73ac461227d496881f244e1b9cfa2be7f7 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 31 Mar 2015 14:11:49 +0100 Subject: [PATCH 417/455] make user list a document so it's easy to navigate by screen readers --- src/static/css/pad.css | 2 +- src/templates/pad.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/static/css/pad.css b/src/static/css/pad.css index ee0a0a96b..01da93321 100644 --- a/src/static/css/pad.css +++ b/src/static/css/pad.css @@ -756,7 +756,7 @@ table#otheruserstable { } .buttonicon:focus{ - border: 1px solid #fff; + border: 1px solid #666; } .buttonicon-bold:before { content: "\e81c"; diff --git a/src/templates/pad.html b/src/templates/pad.html index 32cf72969..babccd0f4 100644 --- a/src/templates/pad.html +++ b/src/templates/pad.html @@ -88,7 +88,7 @@
      -
      +
      From 73073dcbc1e741d4a9e62afad7f76f2838f2237d Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 31 Mar 2015 14:47:00 +0100 Subject: [PATCH 418/455] Timeslider accessibility and Bugfixes --- src/static/css/pad.css | 15 ++++++++++++++- src/static/js/broadcast_slider.js | 1 - src/static/js/pad_editbar.js | 4 ++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/static/css/pad.css b/src/static/css/pad.css index 01da93321..e5e765a1c 100644 --- a/src/static/css/pad.css +++ b/src/static/css/pad.css @@ -137,8 +137,21 @@ a img { top: 1px; } .toolbar ul li a .buttontext { - color: #222; + color: #666; font-size: 14px; + border:none; + background:none; + margin-top:1px; + color:#666; +} +.buttontext::-moz-focus-inner { + padding: 0; + border: 0; +} +.buttontext:focus{ + border: 1px solid #666; +} + } .toolbar ul li a.grouped-left { border-radius: 3px 0 0 3px; diff --git a/src/static/js/broadcast_slider.js b/src/static/js/broadcast_slider.js index 7f0e48bc8..a14181409 100644 --- a/src/static/js/broadcast_slider.js +++ b/src/static/js/broadcast_slider.js @@ -330,7 +330,6 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded) } } else if (code == 32) playpause(); - }); $(window).resize(function() diff --git a/src/static/js/pad_editbar.js b/src/static/js/pad_editbar.js index 75bf40f06..027fce825 100644 --- a/src/static/js/pad_editbar.js +++ b/src/static/js/pad_editbar.js @@ -308,6 +308,10 @@ var padeditbar = (function() var editbarPosition = 0; function bodyKeyEvent(evt){ + // Check we're on a pad and not on the timeslider + // Or some other window I haven't thought about! + if(typeof pad === 'undefined') return false; + // If the event is Alt F9 or Escape & we're already in the editbar menu // Send the users focus back to the pad if((evt.keyCode === 120 && evt.altKey) || evt.keyCode === 27){ From 24e6e1728a020e38e023df0a09ad38941ba0dd82 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 31 Mar 2015 15:00:43 +0100 Subject: [PATCH 419/455] I came to bring the rain.. Let me get my coat... Fix focus on timeslider import export --- src/static/css/timeslider.css | 19 ++++++++++++++++++- src/static/js/pad_editbar.js | 18 ++++++++++++------ src/templates/timeslider.html | 6 +++--- 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/static/css/timeslider.css b/src/static/css/timeslider.css index 49f894210..7bff73411 100644 --- a/src/static/css/timeslider.css +++ b/src/static/css/timeslider.css @@ -78,6 +78,7 @@ width: 44px; text-align:center; vertical-align:middle; + background:none; } #playpause_button { right: 77px; @@ -125,7 +126,7 @@ font-family: fontawesome-etherpad; border-radius:2px; border: #666 solid 1px; - line-height:18px; +/* line-height:18px; */ text-align:center; height:22px; color:#666; @@ -337,3 +338,19 @@ OL { .list-number6 { list-style-type: lower-roman } + +button{ + margin:0; + padding:0; + cursor:pointer; +} + +button::-moz-focus-inner { + padding: 0; + border: 0 +} + +button:focus{ + border: 1px solid #666; +} + diff --git a/src/static/js/pad_editbar.js b/src/static/js/pad_editbar.js index 027fce825..fadbc09dd 100644 --- a/src/static/js/pad_editbar.js +++ b/src/static/js/pad_editbar.js @@ -308,9 +308,6 @@ var padeditbar = (function() var editbarPosition = 0; function bodyKeyEvent(evt){ - // Check we're on a pad and not on the timeslider - // Or some other window I haven't thought about! - if(typeof pad === 'undefined') return false; // If the event is Alt F9 or Escape & we're already in the editbar menu // Send the users focus back to the pad @@ -319,6 +316,10 @@ var padeditbar = (function() // Close any dropdowns we have open.. padeditbar.toggleDropDown("none"); + // Check we're on a pad and not on the timeslider + // Or some other window I haven't thought about! + if(typeof pad === 'undefined') return false; + // Shift focus away from any drop downs $(':focus').blur(); // required to do not try to remove! padeditor.ace.focus(); // Sends focus back to pad @@ -366,9 +367,14 @@ var padeditbar = (function() toolbar.registerCommand("import_export", function () { toolbar.toggleDropDown("import_export", function(){ - setTimeout(function(){ - $('#importfileinput').focus(); - }, 100); + // If Import file input exists then focus on it.. + if($('#importfileinput').length !== 0){ + setTimeout(function(){ + $('#importfileinput').focus(); + }, 100); + }else{ + $('.exportlink').first().focus(); + } }); }); diff --git a/src/templates/timeslider.html b/src/templates/timeslider.html index a619c7021..20688ea8a 100644 --- a/src/templates/timeslider.html +++ b/src/templates/timeslider.html @@ -61,11 +61,11 @@
      -
      +
      -
      -
      + +
      From 498e7f4961379822e7a3ab03cc9963dceddf2e32 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 31 Mar 2015 16:12:05 +0100 Subject: [PATCH 420/455] MVP of alt A behavior to show author of line --- src/static/js/ace2_inner.js | 40 ++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 32492e348..919fa4c78 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -3677,7 +3677,6 @@ function Ace2Inner(){ if (keyCode == 13 && browser.opera && (type == "keypress")){ return; // This stops double enters in Opera but double Tabs still show on single tab keypress, adding keyCode == 9 to this doesn't help as the event is fired twice } - var specialHandled = false; var isTypeForSpecialKey = ((browser.msie || browser.safari || browser.chrome) ? (type == "keydown") : (type == "keypress")); var isTypeForCmdKey = ((browser.msie || browser.safari || browser.chrome) ? (type == "keydown") : (type == "keypress")); @@ -3725,6 +3724,45 @@ function Ace2Inner(){ parent.parent.chat.focus(); evt.preventDefault(); } + if ((!specialHandled) && altKey && keyCode == 65 && type === "keydown"){ + // Alt A shows a gritter popup showing a line author + var lineNumber = rep.selEnd[0]; + var alineAttrs = rep.alines[lineNumber]; + var apool = rep.apool; + + // TODO: support selection ranges + // TODO: Support multiple authors + // TODO: Still work when authorship colors have been cleared + // TODO: i18n + // TODO: There appears to be a race condition or so. + + var author = null; + if (alineAttrs) { + var opIter = Changeset.opIterator(alineAttrs); + if (opIter.hasNext()) { + var op = opIter.next(); + authorId = Changeset.opAttributeValue(op, 'author', apool); + } + } + if(authorId.length === 0){ + author = "not available"; + }else{ + var authorObj = parent.parent.clientVars.collab_client_vars.historicalAuthorData[authorId]; + author = authorObj.name; + if(author === "") author = "not available"; + } + + parent.parent.$.gritter.add({ + // (string | mandatory) the heading of the notification + title: 'Authors', + // (string | mandatory) the text inside the notification + text: 'The author of this line is ' + author, + // (bool | optional) if you want it to fade out on its own or just sit there + sticky: false, + // (int | optional) the time you want it to be alive for before fading out + time: '4000' + }); + } if ((!specialHandled) && isTypeForSpecialKey && keyCode == 8) { // "delete" key; in mozilla, if we're at the beginning of a line, normalize now, From 662216b6cfa9f35ecda27eca83b10acd09b5bd9b Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 31 Mar 2015 16:36:44 +0100 Subject: [PATCH 421/455] slightly different alt stuff --- src/node/utils/toolbar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node/utils/toolbar.js b/src/node/utils/toolbar.js index 5d735323d..3bae0b1c7 100644 --- a/src/node/utils/toolbar.js +++ b/src/node/utils/toolbar.js @@ -99,7 +99,7 @@ _.extend(Button.prototype, { }; return tag("li", liAttributes, tag("a", { "class": this.grouping, "data-l10n-id": this.attributes.localizationId }, - tag("button", { "class": " "+ this.attributes.class, "alt": this.attributes.command, "data-l10n-id": this.attributes.localizationId }) + tag("button", { "class": " "+ this.attributes.class, "data-l10n-id": this.attributes.localizationId }) ) ); } From 37c7c7eabec8327869253c4715b755e8a5aa944b Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 31 Mar 2015 17:06:04 +0100 Subject: [PATCH 422/455] i18n for timeslider steppers --- src/locales/en.json | 4 ++++ src/templates/timeslider.html | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/locales/en.json b/src/locales/en.json index b5713660b..828844b0c 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -121,6 +121,10 @@ "timeslider.version": "Version {{version}}", "timeslider.saved": "Saved {{month}} {{day}}, {{year}}", + "timeslider.playpause": "Playback / Pause Pad Contents", + "timeslider.backrevision":"Go back a revision in this Pad", + "timeslider.forwardrevision":"Go forward a revision in this Pad", + "timeslider.dateformat": "{{month}}/{{day}}/{{year}} {{hours}}:{{minutes}}:{{seconds}}", "timeslider.month.january": "January", "timeslider.month.february": "February", diff --git a/src/templates/timeslider.html b/src/templates/timeslider.html index 20688ea8a..45d7832d1 100644 --- a/src/templates/timeslider.html +++ b/src/templates/timeslider.html @@ -61,11 +61,11 @@
      - +
      - - + +
      From 7e9bc1b7b99cc97e3258e3b4a11ad9051b7397a0 Mon Sep 17 00:00:00 2001 From: LeoVerto Date: Tue, 31 Mar 2015 18:33:47 +0200 Subject: [PATCH 423/455] Fix minor typo in safeRun.sh email report --- bin/safeRun.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/safeRun.sh b/bin/safeRun.sh index 4b3485ba4..519a0b6ef 100755 --- a/bin/safeRun.sh +++ b/bin/safeRun.sh @@ -55,7 +55,7 @@ do TIME_SINCE_LAST_SEND=$(($TIME_NOW - $LAST_EMAIL_SEND)) if [ $TIME_SINCE_LAST_SEND -gt $TIME_BETWEEN_EMAILS ]; then - printf "Server was restared at: $(date)\nThe last 50 lines of the log before the error happens:\n $(tail -n 50 ${LOG})" | mail -s "Pad Server was restarted" $EMAIL_ADDRESS + printf "Server was restarted at: $(date)\nThe last 50 lines of the log before the error happens:\n $(tail -n 50 ${LOG})" | mail -s "Pad Server was restarted" $EMAIL_ADDRESS LAST_EMAIL_SEND=$TIME_NOW fi From 48862dac6f69ff80507c0bad6608968f54430cd6 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 31 Mar 2015 18:50:20 +0100 Subject: [PATCH 424/455] better handling for inputs and left and right arrows --- src/static/js/pad_editbar.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/static/js/pad_editbar.js b/src/static/js/pad_editbar.js index fadbc09dd..e426824e8 100644 --- a/src/static/js/pad_editbar.js +++ b/src/static/js/pad_editbar.js @@ -324,7 +324,6 @@ var padeditbar = (function() $(':focus').blur(); // required to do not try to remove! padeditor.ace.focus(); // Sends focus back to pad // The above focus doesn't always work in FF, you have to hit enter afterwards - // This still needs fixing cake evt.preventDefault(); } @@ -336,13 +335,23 @@ var padeditbar = (function() // On left arrow move to next button in editbar if(evt.keyCode === 37){ + // If a dropdown is visible or we're in an input don't move to the next button + if($('.popup').is(":visible") || evt.target.localName === "input") return; + editbarPosition--; + // Allow focus to shift back to end of row and start of row + if(editbarPosition === -1) editbarPosition = focusItems.length -1; $(focusItems[editbarPosition]).focus() } // On right arrow move to next button in editbar if(evt.keyCode === 39){ + // If a dropdown is visible or we're in an input don't move to the next button + if($('.popup').is(":visible") || evt.target.localName === "input") return; + editbarPosition++; + // Allow focus to shift back to end of row and start of row + if(editbarPosition >= focusItems.length) editbarPosition = 0; $(focusItems[editbarPosition]).focus(); } From 64034ee1c6701e05a57f2d332a2cd3ca58f318dc Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 31 Mar 2015 18:50:50 +0100 Subject: [PATCH 425/455] doh caps --- src/locales/en.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/locales/en.json b/src/locales/en.json index 828844b0c..24c56edf2 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -121,9 +121,9 @@ "timeslider.version": "Version {{version}}", "timeslider.saved": "Saved {{month}} {{day}}, {{year}}", - "timeslider.playpause": "Playback / Pause Pad Contents", - "timeslider.backrevision":"Go back a revision in this Pad", - "timeslider.forwardrevision":"Go forward a revision in this Pad", + "timeslider.playPause": "Playback / Pause Pad Contents", + "timeslider.backRevision":"Go back a revision in this Pad", + "timeslider.forwardRevision":"Go forward a revision in this Pad", "timeslider.dateformat": "{{month}}/{{day}}/{{year}} {{hours}}:{{minutes}}:{{seconds}}", "timeslider.month.january": "January", From e9d8c3b53a3d43096f4a8334de20e47b682111d3 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 31 Mar 2015 20:26:55 +0100 Subject: [PATCH 426/455] expose method for getting a full user list on the client including historical data --- src/static/js/ace2_inner.js | 59 +++++++++++++++++++++++++++++------ src/static/js/pad.js | 4 +++ src/static/js/pad_userlist.js | 24 ++++++++++++++ 3 files changed, 77 insertions(+), 10 deletions(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 919fa4c78..3c6c7cb86 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -3731,32 +3731,71 @@ function Ace2Inner(){ var apool = rep.apool; // TODO: support selection ranges - // TODO: Support multiple authors // TODO: Still work when authorship colors have been cleared // TODO: i18n // TODO: There appears to be a race condition or so. var author = null; if (alineAttrs) { + var authors = []; + var authorNames = []; var opIter = Changeset.opIterator(alineAttrs); - if (opIter.hasNext()) { + + while (opIter.hasNext()){ var op = opIter.next(); authorId = Changeset.opAttributeValue(op, 'author', apool); + + // Only push unique authors and ones with values + if(authors.indexOf(authorId) === -1 && authorId !== ""){ + authors.push(authorId); + } + } + } - if(authorId.length === 0){ - author = "not available"; - }else{ - var authorObj = parent.parent.clientVars.collab_client_vars.historicalAuthorData[authorId]; - author = authorObj.name; - if(author === "") author = "not available"; + + // No author information is available IE on a new pad. + if(authors.length === 0){ + var authorString = "No author information is available"; } + else{ + // Known authors info, both current and historical + var padAuthors = parent.parent.pad.userList(); + var authorObj = {}; + authors.forEach(function(authorId){ + padAuthors.forEach(function(padAuthor){ + // If the person doing the lookup is the author.. + if(padAuthor.userId === authorId){ + if(parent.parent.clientVars.userId === authorId){ + authorObj = { + name: "Me" + } + }else{ + authorObj = padAuthor; + } + } + }); + if(!authorObj){ + author = "Unknown"; + return; + } + author = authorObj.name; + if(!author) author = "Unknown"; + authorNames.push(author); + }) + } + if(authors.length === 1){ + var authorString = "The author of this line is " + authorNames; + } + if(authors.length > 1){ + var authorString = "The authors of this line are " + authorNames.join(" & "); + } parent.parent.$.gritter.add({ // (string | mandatory) the heading of the notification - title: 'Authors', + title: 'Line Authors', // (string | mandatory) the text inside the notification - text: 'The author of this line is ' + author, + text: authorString, // (bool | optional) if you want it to fade out on its own or just sit there sticky: false, // (int | optional) the time you want it to be alive for before fading out diff --git a/src/static/js/pad.js b/src/static/js/pad.js index 0d896a607..5ac9d03d4 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.js @@ -433,6 +433,10 @@ var pad = { { return pad.myUserInfo.name; }, + userList: function() + { + return paduserlist.users(); + }, sendClientReady: function(isReconnect, messageType) { messageType = typeof messageType !== 'undefined' ? messageType : 'CLIENT_READY'; diff --git a/src/static/js/pad_userlist.js b/src/static/js/pad_userlist.js index d306256a2..22dab40a9 100644 --- a/src/static/js/pad_userlist.js +++ b/src/static/js/pad_userlist.js @@ -508,6 +508,30 @@ var paduserlist = (function() }); // }, + users: function(){ + // Returns an object of users who have been on this pad + // Firstly we have to get live data.. + var userList = otherUsersInfo; + // Now we need to add ourselves.. + userList.push(myUserInfo); + // Now we add historical authors + var historical = clientVars.collab_client_vars.historicalAuthorData; + for (var key in historical){ + var userId = historical[key].userId; + // Check we don't already have this author in our array + var exists = false; + + userList.forEach(function(user){ + if(user.userId === userId) exists = true; + }); + + if(exists === false){ + userList.push(historical[key]); + } + + } + return userList; + }, setMyUserInfo: function(info) { //translate the colorId From 733deb613e395543ea98690dee0b7eb16f6c36a8 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 31 Mar 2015 23:51:40 +0100 Subject: [PATCH 427/455] fix font type test --- tests/frontend/specs/font_type.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/frontend/specs/font_type.js b/tests/frontend/specs/font_type.js index 25d9df052..41b1de345 100644 --- a/tests/frontend/specs/font_type.js +++ b/tests/frontend/specs/font_type.js @@ -24,7 +24,7 @@ describe("font select", function(){ //check if font changed to monospace var fontFamily = inner$("body").css("font-family").toLowerCase(); - expect(fontFamily).to.be("monospace"); + expect(fontFamily).to.be("courier new"); done(); }); From fef746e80e010baffc4874f6eb8175ce2453ac15 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 1 Apr 2015 00:19:42 +0100 Subject: [PATCH 428/455] fix import issue with txt files and abiword #2572 --- src/node/handler/ImportHandler.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/node/handler/ImportHandler.js b/src/node/handler/ImportHandler.js index 676986510..2dad8b3d8 100644 --- a/src/node/handler/ImportHandler.js +++ b/src/node/handler/ImportHandler.js @@ -148,6 +148,9 @@ exports.doImport = function(req, res, padId) if(!importHandledByPlugin || !directDatabaseAccess){ var fileEnding = path.extname(srcFile).toLowerCase(); var fileIsHTML = (fileEnding === ".html" || fileEnding === ".htm"); + var fileIsTXT = (fileEnding === ".txt"); + if (fileIsTXT) abiword = false; // Don't use abiword for text files + // See https://github.com/ether/etherpad-lite/issues/2572 if (abiword && !fileIsHTML) { abiword.convertFile(srcFile, destFile, "htm", function(err) { //catch convert errors @@ -213,7 +216,7 @@ exports.doImport = function(req, res, padId) // 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){ @@ -245,7 +248,6 @@ exports.doImport = function(req, res, padId) padManager.getPad(padId, function(err, _pad){ var pad = _pad; padManager.unloadPad(padId); - // direct Database Access means a pad user should perform a switchToPad // and not attempt to recieve updated pad data.. if(!directDatabaseAccess){ From 32a09ff461039b8619accdccb64a72f58127b195 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 1 Apr 2015 13:52:56 +0100 Subject: [PATCH 429/455] tests and fix up sloppy code by original author --- src/node/db/API.js | 10 +-- src/node/handler/APIHandler.js | 4 +- tests/backend/specs/api/chat.js | 113 ++++++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+), 7 deletions(-) create mode 100644 tests/backend/specs/api/chat.js diff --git a/src/node/db/API.js b/src/node/db/API.js index 77546bd2b..edd130e2e 100644 --- a/src/node/db/API.js +++ b/src/node/db/API.js @@ -432,8 +432,8 @@ getChatHistory(padId, start, end), returns a part of or the whole chat-history o Example returns: -{"code":0,"message":"ok","data":{"messages":[{"text":"foo","userId":"a.foo","time":1359199533759,"userName":"test"}, - {"text":"bar","userId":"a.foo","time":1359199534622,"userName":"test"}]}} +{"code":0,"message":"ok","data":{"messages":[{"text":"foo","authorID":"a.foo","time":1359199533759,"userName":"test"}, + {"text":"bar","authorID":"a.foo","time":1359199534622,"userName":"test"}]}} {code: 1, message:"start is higher or equal to the current chatHead", data: null} @@ -495,14 +495,14 @@ exports.getChatHistory = function(padID, start, end, callback) } /** -appendChatMessage(padID, text, userID, time), creates a chat message for the pad id +appendChatMessage(padID, text, authorID, time), creates a chat message for the pad id, time is a timestamp Example returns: {code: 0, message:"ok", data: null {code: 1, message:"padID does not exist", data: null} */ -exports.appendChatMessage = function(padID, text, userID, time, callback) +exports.appendChatMessage = function(padID, text, authorID, time, callback) { //text is required if(typeof text != "string") @@ -516,7 +516,7 @@ exports.appendChatMessage = function(padID, text, userID, time, callback) { if(ERR(err, callback)) return; - pad.appendChatMessage(text, userID, parseInt(time)); + pad.appendChatMessage(text, authorID, parseInt(time)); callback(); }); } diff --git a/src/node/handler/APIHandler.js b/src/node/handler/APIHandler.js index 48aa6cbc8..a9df3abc1 100644 --- a/src/node/handler/APIHandler.js +++ b/src/node/handler/APIHandler.js @@ -438,13 +438,13 @@ var version = , "getChatHistory" : ["padID"] , "getChatHistory" : ["padID", "start", "end"] , "getChatHead" : ["padID"] - , "appendChatMessage" : ["padID", "text", "userID", "time"] + , "appendChatMessage" : ["padID", "text", "authorID", "time"] , "restoreRevision" : ["padID", "rev"] } }; // set the latest available API version here -exports.latestApiVersion = '1.2.11'; +exports.latestApiVersion = '1.2.12'; // exports the versions so it can be used by the new Swagger endpoint exports.version = version; diff --git a/tests/backend/specs/api/chat.js b/tests/backend/specs/api/chat.js new file mode 100644 index 000000000..159f30da9 --- /dev/null +++ b/tests/backend/specs/api/chat.js @@ -0,0 +1,113 @@ +var assert = require('assert') + supertest = require(__dirname+'/../../../../src/node_modules/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'}); +apiKey = apiKey.replace(/\n$/, ""); +var apiVersion = 1; +var authorID = ""; +var padID = makeid(); +var timestamp = Date.now(); + +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) + }); +}) + +// BEGIN GROUP AND AUTHOR TESTS +///////////////////////////////////// +///////////////////////////////////// + +/* Tests performed +-> createPad(padID) + -> createAuthor([name]) -- should return an authorID + -> appendChatMessage(padID, text, userID, time) + -> getChatHead(padID) + -> getChatHistory(padID) +*/ + +describe('createPad', function(){ + it('creates a new Pad', function(done) { + api.get(endPoint('createPad')+"&padID="+padID) + .expect(function(res){ + if(res.body.code !== 0) throw new Error("Unable to create new Pad"); + }) + .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("Unable to create author"); + authorID = res.body.data.authorID; // we will be this author for the rest of the tests + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('appendChatMessage', function(){ + it('Adds a chat message to the pad', function(done) { + api.get(endPoint('appendChatMessage')+"&padID="+padID+"&text=blalblalbha&authorID="+authorID+"&time="+timestamp) + .expect(function(res){ + if(res.body.code !== 0) throw new Error("Unable to create chat message"); + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + + +describe('getChatHead', function(){ + it('Gets the head of chat', function(done) { + api.get(endPoint('getChatHead')+"&padID="+padID) + .expect(function(res){ + if(res.body.data.chatHead !== 0) throw new Error("Chat Head Length is wrong"); + + if(res.body.code !== 0) throw new Error("Unable to get chat head"); + }) + .expect('Content-Type', /json/) + .expect(200, done) + }); +}) + +describe('getChatHistory', function(){ + it('Gets Chat History of a Pad', function(done) { + api.get(endPoint('getChatHistory')+"&padID="+padID) + .expect(function(res){ + if(res.body.data.messages.length !== 1) throw new Error("Chat History Length is wrong"); + if(res.body.code !== 0) throw new Error("Unable to get chat history"); + }) + .expect('Content-Type', /json/) + .expect(200, 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 27ae48686d43925244aa29787f905e254d709fb0 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 1 Apr 2015 13:56:52 +0100 Subject: [PATCH 430/455] reintroduce removed api points that were removed by mistake --- src/node/handler/APIHandler.js | 5 ++++- tests/backend/specs/api/chat.js | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/node/handler/APIHandler.js b/src/node/handler/APIHandler.js index a9df3abc1..b4d242011 100644 --- a/src/node/handler/APIHandler.js +++ b/src/node/handler/APIHandler.js @@ -417,6 +417,9 @@ var version = , "setHTML" : ["padID", "html"] , "getAttributePool" : ["padID"] , "getRevisionsCount" : ["padID"] + , "getSavedRevisionsCount" : ["padID"] + , "listSavedRevisions" : ["padID"] + , "saveRevision" : ["padID", "rev"] , "getRevisionChangeset" : ["padID", "rev"] , "getLastEdited" : ["padID"] , "deletePad" : ["padID"] @@ -435,10 +438,10 @@ var version = , "sendClientsMessage" : ["padID", "msg"] , "listAllGroups" : [] , "checkToken" : [] + , "appendChatMessage" : ["padID", "text", "authorID", "time"] , "getChatHistory" : ["padID"] , "getChatHistory" : ["padID", "start", "end"] , "getChatHead" : ["padID"] - , "appendChatMessage" : ["padID", "text", "authorID", "time"] , "restoreRevision" : ["padID", "rev"] } }; diff --git a/tests/backend/specs/api/chat.js b/tests/backend/specs/api/chat.js index 159f30da9..59b7edc0a 100644 --- a/tests/backend/specs/api/chat.js +++ b/tests/backend/specs/api/chat.js @@ -32,7 +32,7 @@ describe('API Versioning', function(){ /* Tests performed -> createPad(padID) -> createAuthor([name]) -- should return an authorID - -> appendChatMessage(padID, text, userID, time) + -> appendChatMessage(padID, text, authorID, time) -> getChatHead(padID) -> getChatHistory(padID) */ From f6b4b5603dfe34ef04f88166579f81e0abcf64ec Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 2 Apr 2015 00:03:34 +0100 Subject: [PATCH 431/455] fix timeslider title issues --- src/static/js/timeslider.js | 6 ++++++ src/templates/timeslider.html | 6 +++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/static/js/timeslider.js b/src/static/js/timeslider.js index ec237df5e..580a7a03d 100644 --- a/src/static/js/timeslider.js +++ b/src/static/js/timeslider.js @@ -157,6 +157,12 @@ function handleClientVars(message) fireWhenAllScriptsAreLoaded[i](); } $("#ui-slider-handle").css('left', $("#ui-slider-bar").width() - 2); + + // Translate some strings where we only want to set the title not the actual values + $('#playpause_button_icon').attr("title", html10n.get("timeslider.playPause")); + $('#leftstep').attr("title", html10n.get("timeslider.backRevision")); + $('#rightstep').attr("title", html10n.get("timeslider.forwardRevision")); + } exports.baseURL = ''; diff --git a/src/templates/timeslider.html b/src/templates/timeslider.html index 45d7832d1..20688ea8a 100644 --- a/src/templates/timeslider.html +++ b/src/templates/timeslider.html @@ -61,11 +61,11 @@
      - +
      - - + +
      From 644536b27b5abdc7dac1c36e489bbc328701772f Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 2 Apr 2015 13:16:49 +0100 Subject: [PATCH 432/455] fix for #2574 --- src/static/css/iframe_editor.css | 13 ++++++++++++- src/static/js/ace2_inner.js | 5 ++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/static/css/iframe_editor.css b/src/static/css/iframe_editor.css index eb69364bd..c32ae3f06 100644 --- a/src/static/css/iframe_editor.css +++ b/src/static/css/iframe_editor.css @@ -98,7 +98,18 @@ body.grayedout { background-color: #eee !important } } body.doesWrap { - white-space: pre-wrap; /*Must be pre-wrap to keep trailing spaces. Otherwise you get a zombie caret, walking around your screen (see #1766), WARNING: Enabling this causes Paste as plain text in Chrome to remove line breaks, this is probably undesirable */ + /* white-space: pre-wrap; */ + + /* + Must be pre-wrap to keep trailing spaces. Otherwise you get a zombie caret, + walking around your screen (see #1766). + WARNING: Enabling this causes Paste as plain text in Chrome to remove line breaks + this is probably undesirable + WARNING: This causes copy & paste events to lose bold etc. attributes + NOTE: The walking-zombie caret issue seems to have been fixed in FF upstream + so let's try diabling pre-wrap and see how we get on now. + For more details see: https://github.com/ether/etherpad-lite/issues/2574 + */ word-wrap: break-word; /* fix for issue #1648 - firefox not wrapping long lines (without spaces) correctly */ } diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 44bf2bed3..14ebc404d 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -608,8 +608,11 @@ function Ace2Inner(){ // Chrome can't handle the truth.. If CSS rule white-space:pre-wrap // is true then any paste event will insert two lines.. + // Sadly this will mean you get a walking Caret in Chrome when clicking on a URL + // So this has to be set to pre-wrap ;( + // We need to file a bug w/ the Chromium team. if(browser.chrome){ - $("#innerdocbody").css({"white-space":"normal"}); + $("#innerdocbody").css({"white-space":"pre-wrap"}); } } From 85fc012bac0dbeefac611314f4ab35f276de871b Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 2 Apr 2015 15:13:16 +0100 Subject: [PATCH 433/455] change to alt shit and a to show authors --- src/static/js/ace2_inner.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index eef99b1b8..b37c737cd 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -3616,6 +3616,7 @@ function Ace2Inner(){ var keyCode = evt.keyCode; var which = evt.which; var altKey = evt.altKey; + var shiftKey = evt.shiftKey; // prevent ESC key if (keyCode == 27) @@ -3703,8 +3704,8 @@ function Ace2Inner(){ parent.parent.chat.focus(); evt.preventDefault(); } - if ((!specialHandled) && altKey && keyCode == 65 && type === "keydown"){ - // Alt A shows a gritter popup showing a line author + if ((!specialHandled) && altKey && shiftKey && keyCode == 65 && type === "keydown"){ + // Alt-Shift-A shows a gritter popup showing a line author var lineNumber = rep.selEnd[0]; var alineAttrs = rep.alines[lineNumber]; var apool = rep.apool; From f79e2c7de201ed948e827437c18aa96f312a6b51 Mon Sep 17 00:00:00 2001 From: John McLear Date: Fri, 3 Apr 2015 12:29:47 +0100 Subject: [PATCH 434/455] final accessibility for Timeslider hopefully --- src/static/js/broadcast_slider.js | 5 ++ src/static/js/pad_editbar.js | 84 ++++++++++++++++++------------- 2 files changed, 54 insertions(+), 35 deletions(-) diff --git a/src/static/js/broadcast_slider.js b/src/static/js/broadcast_slider.js index a14181409..eff20b52e 100644 --- a/src/static/js/broadcast_slider.js +++ b/src/static/js/broadcast_slider.js @@ -290,6 +290,11 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded) $(document).keyup(function(e) { + // If focus is on editbar, don't do anything + var target = $(':focus'); + if($(target).parents(".toolbar").length === 1){ + return; + } var code = -1; if (!e) var e = window.event; if (e.keyCode) code = e.keyCode; diff --git a/src/static/js/pad_editbar.js b/src/static/js/pad_editbar.js index e426824e8..e418969ea 100644 --- a/src/static/js/pad_editbar.js +++ b/src/static/js/pad_editbar.js @@ -312,47 +312,61 @@ var padeditbar = (function() // If the event is Alt F9 or Escape & we're already in the editbar menu // Send the users focus back to the pad if((evt.keyCode === 120 && evt.altKey) || evt.keyCode === 27){ - // If we're in the editbar already.. - // Close any dropdowns we have open.. - padeditbar.toggleDropDown("none"); - - // Check we're on a pad and not on the timeslider - // Or some other window I haven't thought about! - if(typeof pad === 'undefined') return false; - - // Shift focus away from any drop downs - $(':focus').blur(); // required to do not try to remove! - padeditor.ace.focus(); // Sends focus back to pad - // The above focus doesn't always work in FF, you have to hit enter afterwards - evt.preventDefault(); + if($(':focus').parents(".toolbar").length === 1){ + // If we're in the editbar already.. + // Close any dropdowns we have open.. + padeditbar.toggleDropDown("none"); + // Check we're on a pad and not on the timeslider + // Or some other window I haven't thought about! + if(typeof pad === 'undefined'){ + // Timeslider probably.. + // Shift focus away from any drop downs + $(':focus').blur(); // required to do not try to remove! + $('#padmain').focus(); // Focus back onto the pad + }else{ + // Shift focus away from any drop downs + $(':focus').blur(); // required to do not try to remove! + padeditor.ace.focus(); // Sends focus back to pad + // The above focus doesn't always work in FF, you have to hit enter afterwards + evt.preventDefault(); + } + }else{ + // Focus on the editbar :) + var firstEditbarElement = parent.parent.$('#editbar').children("ul").first().children().first().children().first().children().first(); + $(this).blur(); + firstEditbarElement.focus(); + evt.preventDefault(); + } } + // Are we in the toolbar?? + if($(':focus').parents(".toolbar").length === 1){ + // On arrow keys go to next/previous button item in editbar + if(evt.keyCode !== 39 && evt.keyCode !== 37) return; - // On arrow keys go to next/previous button item in editbar - if(evt.keyCode !== 39 && evt.keyCode !== 37) return; + // Get all the focusable items in the editbar + var focusItems = $('#editbar').find('button, select'); - // Get all the focusable items in the editbar - var focusItems = $('#editbar').find('button, select'); + // On left arrow move to next button in editbar + if(evt.keyCode === 37){ + // If a dropdown is visible or we're in an input don't move to the next button + if($('.popup').is(":visible") || evt.target.localName === "input") return; - // On left arrow move to next button in editbar - if(evt.keyCode === 37){ - // If a dropdown is visible or we're in an input don't move to the next button - if($('.popup').is(":visible") || evt.target.localName === "input") return; + editbarPosition--; + // Allow focus to shift back to end of row and start of row + if(editbarPosition === -1) editbarPosition = focusItems.length -1; + $(focusItems[editbarPosition]).focus() + } - editbarPosition--; - // Allow focus to shift back to end of row and start of row - if(editbarPosition === -1) editbarPosition = focusItems.length -1; - $(focusItems[editbarPosition]).focus() - } + // On right arrow move to next button in editbar + if(evt.keyCode === 39){ + // If a dropdown is visible or we're in an input don't move to the next button + if($('.popup').is(":visible") || evt.target.localName === "input") return; - // On right arrow move to next button in editbar - if(evt.keyCode === 39){ - // If a dropdown is visible or we're in an input don't move to the next button - if($('.popup').is(":visible") || evt.target.localName === "input") return; - - editbarPosition++; - // Allow focus to shift back to end of row and start of row - if(editbarPosition >= focusItems.length) editbarPosition = 0; - $(focusItems[editbarPosition]).focus(); + editbarPosition++; + // Allow focus to shift back to end of row and start of row + if(editbarPosition >= focusItems.length) editbarPosition = 0; + $(focusItems[editbarPosition]).focus(); + } } } From 9090d76dcf65492f99385b1e9a81e20d34cd2630 Mon Sep 17 00:00:00 2001 From: John McLear Date: Fri, 3 Apr 2015 12:33:31 +0100 Subject: [PATCH 435/455] temp solution for alt 9 to show editbar label --- src/templates/pad.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/templates/pad.html b/src/templates/pad.html index babccd0f4..cdaeb058d 100644 --- a/src/templates/pad.html +++ b/src/templates/pad.html @@ -56,7 +56,7 @@ <% e.begin_block("body"); %> -
      +
      From c65ddad7d6727e9994a32ab2f450e9dd23cfc240 Mon Sep 17 00:00:00 2001 From: John McLear Date: Fri, 3 Apr 2015 13:06:03 +0100 Subject: [PATCH 436/455] title for chat --- src/templates/pad.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/templates/pad.html b/src/templates/pad.html index cdaeb058d..697caf3b3 100644 --- a/src/templates/pad.html +++ b/src/templates/pad.html @@ -321,7 +321,7 @@ <% e.end_block(); %>
      -
      +
      0 From dfd17d1aba8e3e803e6cb951f7b0e8625ce39143 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Dubigny?= Date: Sat, 4 Apr 2015 17:14:59 +0200 Subject: [PATCH 437/455] Update installDeps.sh --- bin/installDeps.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/installDeps.sh b/bin/installDeps.sh index 04c4a02a9..914e45a45 100755 --- a/bin/installDeps.sh +++ b/bin/installDeps.sh @@ -103,7 +103,7 @@ if [ $DOWNLOAD_JQUERY = "true" ]; then fi #Remove all minified data to force node creating it new -echo "Clear minfified cache..." +echo "Clear minified cache..." rm -f var/minified* echo "Ensure custom css/js files are created..." From 119fc82387695a5bddd1ab441d9022ff50594684 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Dubigny?= Date: Sat, 4 Apr 2015 17:30:41 +0200 Subject: [PATCH 438/455] Update installDeps.sh --- bin/installDeps.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/installDeps.sh b/bin/installDeps.sh index 914e45a45..a5e4d5abd 100755 --- a/bin/installDeps.sh +++ b/bin/installDeps.sh @@ -103,7 +103,7 @@ if [ $DOWNLOAD_JQUERY = "true" ]; then fi #Remove all minified data to force node creating it new -echo "Clear minified cache..." +echo "Clearing minified cache..." rm -f var/minified* echo "Ensure custom css/js files are created..." From db0bcdd696282ec5d8ab7ab4c2cbeb5043131a96 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 5 Apr 2015 13:39:20 +0100 Subject: [PATCH 439/455] fix issue where focus on timeslider wouldnt work --- src/static/css/pad.css | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/static/css/pad.css b/src/static/css/pad.css index e5e765a1c..2e68ed00f 100644 --- a/src/static/css/pad.css +++ b/src/static/css/pad.css @@ -144,15 +144,17 @@ a img { margin-top:1px; color:#666; } + .buttontext::-moz-focus-inner { padding: 0; border: 0; } + .buttontext:focus{ - border: 1px solid #666; + /* Not sure why important is required here but it is */ + border: 1px solid #666 !important; } -} .toolbar ul li a.grouped-left { border-radius: 3px 0 0 3px; } From e49bb4017c5cd054ca2b1880c78148e10417de1f Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 5 Apr 2015 13:42:26 +0100 Subject: [PATCH 440/455] use ctrl shift 2 instead of alt a --- src/static/js/ace2_inner.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index b37c737cd..7773074ea 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -3704,8 +3704,8 @@ function Ace2Inner(){ parent.parent.chat.focus(); evt.preventDefault(); } - if ((!specialHandled) && altKey && shiftKey && keyCode == 65 && type === "keydown"){ - // Alt-Shift-A shows a gritter popup showing a line author + if ((!specialHandled) && evt.ctrlKey && shiftKey && keyCode == 50 && type === "keydown"){ + // Control-Shift-2 shows a gritter popup showing a line author var lineNumber = rep.selEnd[0]; var alineAttrs = rep.alines[lineNumber]; var apool = rep.apool; From 41bb4ef094ebd22c5d0068dd9e0ca13816519c81 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 5 Apr 2015 14:03:09 +0100 Subject: [PATCH 441/455] support for open dyslexic font --- src/locales/en.json | 1 + src/static/css/pad.css | 6 ++++++ src/static/font/opendyslexic.otf | Bin 0 -> 48076 bytes src/static/js/pad.js | 2 +- src/static/js/pad_editor.js | 3 ++- src/templates/pad.html | 1 + 6 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 src/static/font/opendyslexic.otf diff --git a/src/locales/en.json b/src/locales/en.json index 24c56edf2..3e16c5de0 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -38,6 +38,7 @@ "pad.settings.rtlcheck": "Read content from right to left?", "pad.settings.fontType": "Font type:", "pad.settings.fontType.normal": "Normal", + "pad.settings.fontType.opendyslexic": "Open Dyslexic", "pad.settings.fontType.monospaced": "Monospace", "pad.settings.fontType.comicsans": "Comic Sans", "pad.settings.fontType.couriernew": "Courier New", diff --git a/src/static/css/pad.css b/src/static/css/pad.css index 2e68ed00f..96e2122b8 100644 --- a/src/static/css/pad.css +++ b/src/static/css/pad.css @@ -1284,3 +1284,9 @@ input[type=checkbox] { .hideControlsEditbar{ display:none !important; } + + +@font-face { + font-family: opendyslexic; + src: url("../font/opendyslexic.otf") format("opentype"); +} diff --git a/src/static/font/opendyslexic.otf b/src/static/font/opendyslexic.otf new file mode 100644 index 0000000000000000000000000000000000000000..1a7c9d411b225f4e596478314151e9c4d57e3a9f GIT binary patch literal 48076 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 F{~vP7>ly$6 literal 0 HcmV?d00001 diff --git a/src/static/js/pad.js b/src/static/js/pad.js index 5ac9d03d4..f1de80f0f 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.js @@ -581,7 +581,7 @@ var pad = { pad.changeViewOption('rtlIsTrue', true); } - var fonts = ['useMonospaceFont', 'useComicSansFont', 'useCourierNewFont', 'useGeorgiaFont', 'useImpactFont', + var fonts = ['useMonospaceFont', 'useOpenDyslexicFont', 'useComicSansFont', 'useCourierNewFont', 'useGeorgiaFont', 'useImpactFont', 'useLucidaFont', 'useLucidaSansFont', 'usePalatinoFont', 'useTahomaFont', 'useTimesNewRomanFont', 'useTrebuchetFont', 'useVerdanaFont', 'useSymbolFont', 'useWebdingsFont', 'useWingDingsFont', 'useSansSerifFont', 'useSerifFont']; diff --git a/src/static/js/pad_editor.js b/src/static/js/pad_editor.js index ca7dc2b0b..b1ea09f72 100644 --- a/src/static/js/pad_editor.js +++ b/src/static/js/pad_editor.js @@ -30,7 +30,7 @@ var padeditor = (function() var settings = undefined; // Array of available fonts - var fonts = ['useMonospaceFont', 'useComicSansFont', 'useCourierNewFont', 'useGeorgiaFont', 'useImpactFont', + var fonts = ['useMonospaceFont', 'useOpenDyslexicFont', 'useComicSansFont', 'useCourierNewFont', 'useGeorgiaFont', 'useImpactFont', 'useLucidaFont', 'useLucidaSansFont', 'usePalatinoFont', 'useTahomaFont', 'useTimesNewRomanFont', 'useTrebuchetFont', 'useVerdanaFont', 'useSymbolFont', 'useWebdingsFont', 'useWingDingsFont', 'useSansSerifFont', 'useSerifFont']; @@ -163,6 +163,7 @@ var padeditor = (function() font = font.replace("Font",""); font = font.toLowerCase(); if(font === "monospace") self.ace.setProperty("textface", "Courier new"); + if(font === "opendyslexic") self.ace.setProperty("textface", "OpenDyslexic"); if(font === "comicsans") self.ace.setProperty("textface", "Comic Sans MS"); if(font === "georgia") self.ace.setProperty("textface", "Georgia"); if(font === "impact") self.ace.setProperty("textface", "Impact"); diff --git a/src/templates/pad.html b/src/templates/pad.html index 697caf3b3..dd260414e 100644 --- a/src/templates/pad.html +++ b/src/templates/pad.html @@ -160,6 +160,7 @@
      + + + + +
      -
      -

      Etherpad Git Commit

      -

      <%= gitCommit %>

      +

      Etherpad version

      +

      Version number: <%= epVersion %>

      +

      Git sha: <%= gitCommit %>

      Installed plugins

      <%- plugins.formatPlugins().replace(", ","\n") %>
      From db5bdc87194cc8a790dd9d7cd656b72d42c5f285 Mon Sep 17 00:00:00 2001 From: Stefan Date: Sat, 11 Apr 2015 00:13:04 +0200 Subject: [PATCH 451/455] Log version number and git-sha on server start --- src/node/hooks/express.js | 2 ++ src/node/hooks/express/adminplugins.js | 2 +- src/node/utils/Settings.js | 5 +++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/node/hooks/express.js b/src/node/hooks/express.js index 3275bd3ff..bf8494196 100644 --- a/src/node/hooks/express.js +++ b/src/node/hooks/express.js @@ -13,6 +13,8 @@ exports.createServer = function () { console.log("Report bugs at https://github.com/ether/etherpad-lite/issues") serverName = "Etherpad " + settings.getGitCommit() + " (http://etherpad.org)"; + + console.log("Your Etherpad version is " + settings.getEpVersion() + " (" + settings.getGitCommit() + ")"); exports.restartServer(); diff --git a/src/node/hooks/express/adminplugins.js b/src/node/hooks/express/adminplugins.js index c49d6621e..1ae8d7b50 100644 --- a/src/node/hooks/express/adminplugins.js +++ b/src/node/hooks/express/adminplugins.js @@ -4,7 +4,6 @@ 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 epVersion = require('ep_etherpad-lite/package.json').version; exports.expressCreateServer = function (hook_name, args, cb) { args.app.get('/admin/plugins', function(req, res) { @@ -18,6 +17,7 @@ exports.expressCreateServer = function (hook_name, args, cb) { }); args.app.get('/admin/plugins/info', function(req, res) { var gitCommit = settings.getGitCommit(); + var epVersion = settings.getEpVersion(); res.send( eejs.require("ep_etherpad-lite/templates/admin/plugins-info.html", { gitCommit: gitCommit, diff --git a/src/node/utils/Settings.js b/src/node/utils/Settings.js index 07f5055c0..7e0e6c5a1 100644 --- a/src/node/utils/Settings.js +++ b/src/node/utils/Settings.js @@ -207,6 +207,11 @@ exports.getGitCommit = function() { return version; } +// Return etherpad version from package.json +exports.getEpVersion = function() { + return require('ep_etherpad-lite/package.json').version; +} + exports.reloadSettings = function reloadSettings() { // Discover where the settings file lives var settingsFilename = argv.settings || "settings.json"; From d3277deafcf7c57775ee154236dda938cc5c6329 Mon Sep 17 00:00:00 2001 From: Stefan Date: Sat, 11 Apr 2015 00:32:18 +0200 Subject: [PATCH 452/455] Increase etherpad version to 1.5.3 --- src/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/package.json b/src/package.json index 4070431e4..4e3c6f24e 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.5.2" + "version" : "1.5.3" } From 5409eb314c4e072b9760b8d30b985fa0bb96a006 Mon Sep 17 00:00:00 2001 From: Tom Hunkapiller Date: Fri, 10 Apr 2015 19:25:52 -0500 Subject: [PATCH 453/455] fix an issue in the path handling that allowed directory traversal --- src/node/hooks/express/tests.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/node/hooks/express/tests.js b/src/node/hooks/express/tests.js index 3157d68ed..dfe02c2af 100644 --- a/src/node/hooks/express/tests.js +++ b/src/node/hooks/express/tests.js @@ -23,6 +23,7 @@ exports.expressCreateServer = function (hook_name, args, cb) { }); + var rootTestFolder = path.join(npm.root, "..", "/tests/frontend/"); var url2FilePath = function(url){ var subPath = url.substr("/tests/frontend".length); if (subPath == ""){ @@ -30,8 +31,11 @@ exports.expressCreateServer = function (hook_name, args, cb) { } subPath = subPath.split("?")[0]; - var filePath = path.normalize(npm.root + "/../tests/frontend/") - filePath += subPath.replace("..", ""); + var filePath = path.normalize(path.join(rootTestFolder, subPath)); + // make sure we jail the paths to the test folder, otherwise serve index + if (filePath.indexOf(rootTestFolder) !== 0) { + filePath = path.normalize(path.join(rootTestFolder, "index.html")); + } return filePath; } From 0b0a34908197a0a58c5d16765661000bd24cb39c Mon Sep 17 00:00:00 2001 From: Tom Hunkapiller Date: Fri, 10 Apr 2015 20:19:26 -0500 Subject: [PATCH 454/455] clearer comments about the path handling behavior --- src/node/hooks/express/tests.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/node/hooks/express/tests.js b/src/node/hooks/express/tests.js index dfe02c2af..151c99fab 100644 --- a/src/node/hooks/express/tests.js +++ b/src/node/hooks/express/tests.js @@ -23,7 +23,10 @@ exports.expressCreateServer = function (hook_name, args, cb) { }); - var rootTestFolder = path.join(npm.root, "..", "/tests/frontend/"); + + // path.join seems to normalize by default, but we'll just be explicit + var rootTestFolder = path.normalize(path.join(npm.root, "../tests/frontend/")); + var url2FilePath = function(url){ var subPath = url.substr("/tests/frontend".length); if (subPath == ""){ @@ -34,7 +37,7 @@ exports.expressCreateServer = function (hook_name, args, cb) { var filePath = path.normalize(path.join(rootTestFolder, subPath)); // make sure we jail the paths to the test folder, otherwise serve index if (filePath.indexOf(rootTestFolder) !== 0) { - filePath = path.normalize(path.join(rootTestFolder, "index.html")); + filePath = path.join(rootTestFolder, "index.html"); } return filePath; } From 1b9a51c879bfa33f5dc509fa923c202ccdd60c57 Mon Sep 17 00:00:00 2001 From: Stefan Date: Sat, 11 Apr 2015 10:19:02 +0200 Subject: [PATCH 455/455] Release version 1.5.4 --- CHANGELOG.md | 3 +++ src/package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d06aa871..926e3d3c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# 1.5.4 + * SECURITY: Also don't allow read files on directory traversal on frontend tests path + # 1.5.3 * NEW: Accessibility support for Screen readers, includes new fonts and keyboard shortcuts * NEW: API endpoint for Append Chat Message and Chat Backend Tests diff --git a/src/package.json b/src/package.json index 4e3c6f24e..ed9ba9578 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.5.3" + "version" : "1.5.4" }
      + + + +