mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-04-27 02:46:15 -04:00
resolve conflict with async test
This commit is contained in:
commit
a908bbee8e
14 changed files with 229 additions and 1139 deletions
|
@ -11,24 +11,42 @@
|
|||
"Sebastian Wallroth",
|
||||
"Thargon",
|
||||
"Tim.krieger",
|
||||
"Wikinaut"
|
||||
"Wikinaut",
|
||||
"Zunkelty"
|
||||
]
|
||||
},
|
||||
"admin.page-title": "Admin Dashboard - Etherpad",
|
||||
"admin_plugins": "Plugins verwalten",
|
||||
"admin_plugins.available": "Verfügbare Plugins",
|
||||
"admin_plugins.available_not-found": "Keine Plugins gefunden.",
|
||||
"admin_plugins.available_fetching": "Wird abgerufen...",
|
||||
"admin_plugins.available_install.value": "Installieren",
|
||||
"admin_plugins.available_search.placeholder": "Suche nach Plugins zum Installieren",
|
||||
"admin_plugins.description": "Beschreibung",
|
||||
"admin_plugins.installed": "Installierte Plugins",
|
||||
"admin_plugins.installed_fetching": "Rufe installierte Plugins ab...",
|
||||
"admin_plugins.installed_nothing": "Du hast bisher noch keine Plugins installiert.",
|
||||
"admin_plugins.installed_uninstall.value": "Deinstallieren",
|
||||
"admin_plugins.last-update": "Letze Aktualisierung",
|
||||
"admin_plugins.name": "Name",
|
||||
"admin_plugins.page-title": "Plugin Manager - Etherpad",
|
||||
"admin_plugins.version": "Version",
|
||||
"admin_plugins_info": "Hilfestellung",
|
||||
"admin_plugins_info.hooks": "Installierte Hooks",
|
||||
"admin_plugins_info.hooks_client": "Client-seitige Hooks",
|
||||
"admin_plugins_info.hooks_server": "Server-seitige Hooks",
|
||||
"admin_plugins_info.parts": "Installierte Teile",
|
||||
"admin_plugins_info.plugins": "Installierte Plugins",
|
||||
"admin_plugins_info.page-title": "Plugin Informationen - Etherpad",
|
||||
"admin_plugins_info.version": "Etherpad Version",
|
||||
"admin_plugins_info.version_latest": "Neueste Version",
|
||||
"admin_plugins_info.version_number": "Versionsnummer",
|
||||
"admin_settings": "Einstellungen",
|
||||
"admin_settings.current": "Derzeitige Konfiguration",
|
||||
"admin_settings.current_example-devel": "Beispielhafte Entwicklungseinstellungs-Templates",
|
||||
"admin_settings.current_restart.value": "Etherpad neustarten",
|
||||
"admin_settings.current_save.value": "Einstellungen speichern",
|
||||
"admin_settings.page-title": "Einstellungen - Etherpad",
|
||||
"index.newPad": "Neues Pad",
|
||||
"index.createOpenPad": "oder ein Pad mit folgendem Namen erstellen/öffnen:",
|
||||
"index.openPad": "Öffne ein vorhandenes Pad mit folgendem Namen:",
|
||||
|
@ -78,7 +96,7 @@
|
|||
"pad.importExport.exportopen": "ODF (Open Document Format)",
|
||||
"pad.importExport.abiword.innerHTML": "Du kannst nur aus reinen Text- oder HTML-Formaten importieren. Für umfangreichere Importfunktionen <a href=\"https://github.com/ether/etherpad-lite/wiki/How-to-enable-importing-and-exporting-different-file-formats-with-AbiWord\">muss AbiWord oder LibreOffice auf dem Server installiert werden</a>.",
|
||||
"pad.modals.connected": "Verbunden.",
|
||||
"pad.modals.reconnecting": "Wiederherstellen der Verbindung …",
|
||||
"pad.modals.reconnecting": "Dein Pad wird neu verbunden...",
|
||||
"pad.modals.forcereconnect": "Erneutes Verbinden erzwingen",
|
||||
"pad.modals.reconnecttimer": "Versuche Neuverbindung in",
|
||||
"pad.modals.cancel": "Abbrechen",
|
||||
|
@ -102,6 +120,7 @@
|
|||
"pad.modals.deleted.explanation": "Dieses Pad wurde entfernt.",
|
||||
"pad.modals.rateLimited": "Begrenzte Rate.",
|
||||
"pad.modals.rateLimited.explanation": "Sie haben zu viele Nachrichten an dieses Pad gesendet, so dass die Verbindung unterbrochen wurde.",
|
||||
"pad.modals.rejected.explanation": "Der Server hat eine Nachricht abgelehnt, die von deinem Browser gesendet wurde.",
|
||||
"pad.modals.disconnected": "Ihre Verbindung wurde getrennt.",
|
||||
"pad.modals.disconnected.explanation": "Die Verbindung zum Server wurde unterbrochen.",
|
||||
"pad.modals.disconnected.cause": "Möglicherweise ist der Server nicht erreichbar. Bitte benachrichtige den Dienstadministrator, falls dies weiterhin passiert.",
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
"VezonThunder"
|
||||
]
|
||||
},
|
||||
"admin_plugins.available": "Saatavilla olevat liitännäiset",
|
||||
"admin_plugins.available_install.value": "Lataa",
|
||||
"admin_plugins.available_search.placeholder": "Etsi asennettavia laajennuksia",
|
||||
"admin_plugins.description": "Kuvaus",
|
||||
|
@ -92,7 +93,7 @@
|
|||
"pad.importExport.exportopen": "ODF (Open Document Format)",
|
||||
"pad.importExport.abiword.innerHTML": "Tuonti on tuettu vain HTML- ja raakatekstitiedostoista. Monipuoliset tuontiominaisuudet ovat käytettävissä <a href=\"https://github.com/ether/etherpad-lite/wiki/How-to-enable-importing-and-exporting-different-file-formats-with-AbiWord\">asentamalla AbiWordin tai LibreOfficen</a>.",
|
||||
"pad.modals.connected": "Yhdistetty.",
|
||||
"pad.modals.reconnecting": "Muodostetaan yhteyttä muistioon uudelleen...",
|
||||
"pad.modals.reconnecting": "Muodostetaan yhteyttä muistioon uudelleen…",
|
||||
"pad.modals.forcereconnect": "Pakota yhdistämään uudelleen",
|
||||
"pad.modals.reconnecttimer": "Yritetään yhdistää uudelleen",
|
||||
"pad.modals.cancel": "Peruuta",
|
||||
|
|
|
@ -9,6 +9,10 @@
|
|||
"Upwinxp"
|
||||
]
|
||||
},
|
||||
"admin_plugins.last-update": "Zadnja posodobitev",
|
||||
"admin_plugins.name": "Ime",
|
||||
"admin_plugins.version": "Različica",
|
||||
"admin_settings": "Nastavitve",
|
||||
"index.newPad": "Nov dokument",
|
||||
"index.createOpenPad": "ali pa ustvari/odpri dokument z imenom:",
|
||||
"pad.toolbar.bold.title": "Krepko (Ctrl-B)",
|
||||
|
@ -54,7 +58,7 @@
|
|||
"pad.importExport.exportword": "DOC (zapis Microsoft Word)",
|
||||
"pad.importExport.exportpdf": "PDF (zapis Acrobat PDF)",
|
||||
"pad.importExport.exportopen": "ODF (zapis Open Document)",
|
||||
"pad.importExport.abiword.innerHTML": "Uvoziti je mogoče le običajno neoblikovano besedilo in zapise HTML. Za naprednejše zmožnosti namestite <a href=\"https://github.com/ether/etherpad-lite/wiki/How-to-enable-importing-and-exporting-different-file-formats-with-AbiWord\">program AbiWord</a>.",
|
||||
"pad.importExport.abiword.innerHTML": "Uvoziti je mogoče le neoblikovano besedilo in zapise HTML. Za naprednejše možnosti uvoza namestite program <a href=\"https://github.com/ether/etherpad-lite/wiki/How-to-enable-importing-and-exporting-different-file-formats-with-AbiWord\">AbiWord</a>.",
|
||||
"pad.modals.connected": "Povezano.",
|
||||
"pad.modals.reconnecting": "Poteka povezovanje z dokumentom ...",
|
||||
"pad.modals.forcereconnect": "Vsili ponovno povezavo",
|
||||
|
@ -64,7 +68,7 @@
|
|||
"pad.modals.userdup.explanation": "Videti je, da je ta dokument odprt v več kot enem oknu brskalnika na tem računalniku.",
|
||||
"pad.modals.userdup.advice": "Ponovno vzpostavite povezavo in uporabljajte to okno.",
|
||||
"pad.modals.unauth": "Nepooblaščen dostop",
|
||||
"pad.modals.unauth.explanation": "Med pregledovanjem te strani so se dovoljenja za ogled spremenila. Poskusite se ponovno povezati.",
|
||||
"pad.modals.unauth.explanation": "Med ogledovanjem strani so se dovoljenja za ogled spremenila. Poskusite se znova povezati.",
|
||||
"pad.modals.looping.explanation": "Zaznane so težave pri komunikaciji s strežnikom za usklajevanje.",
|
||||
"pad.modals.looping.cause": "Morda ste se povezali preko neustrezno nastavljenega požarnega zidu ali posredniškega strežnika.",
|
||||
"pad.modals.initsocketfail": "Strežnik je nedosegljiv.",
|
||||
|
|
|
@ -1217,7 +1217,7 @@ const handleChangesetRequest = async (socket, message) => {
|
|||
socket.json.send({type: 'CHANGESET_REQ', data});
|
||||
} catch (err) {
|
||||
console.error(`Error while handling a changeset request for ${padIds.padId}`,
|
||||
err.toString(), message.data);
|
||||
err.toString(), message.data);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
990
src/package-lock.json
generated
990
src/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -78,18 +78,17 @@
|
|||
"etherpad-lite": "node/server.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^7.15.0",
|
||||
"eslint-config-etherpad": "^1.0.20",
|
||||
"eslint": "^7.18.0",
|
||||
"eslint-config-etherpad": "^1.0.24",
|
||||
"eslint-plugin-eslint-comments": "^3.2.0",
|
||||
"eslint-plugin-mocha": "^8.0.0",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-prefer-arrow": "^1.2.2",
|
||||
"eslint-plugin-prefer-arrow": "^1.2.3",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
"eslint-plugin-you-dont-need-lodash-underscore": "^6.10.0",
|
||||
"etherpad-cli-client": "0.0.9",
|
||||
"mocha": "7.1.2",
|
||||
"mocha-froth": "^0.2.10",
|
||||
"nyc": "15.0.1",
|
||||
"set-cookie-parser": "^2.4.6",
|
||||
"sinon": "^9.2.0",
|
||||
"superagent": "^3.8.3",
|
||||
|
@ -148,8 +147,8 @@
|
|||
},
|
||||
"scripts": {
|
||||
"lint": "eslint .",
|
||||
"test": "nyc mocha --timeout 120000 --recursive ../tests/backend/specs ../node_modules/ep_*/static/tests/backend/specs",
|
||||
"test-container": "nyc mocha --timeout 5000 ../tests/container/specs/api"
|
||||
"test": "mocha --timeout 120000 --recursive ../tests/backend/specs ../node_modules/ep_*/static/tests/backend/specs",
|
||||
"test-container": "mocha --timeout 5000 ../tests/container/specs/api"
|
||||
},
|
||||
"version": "1.8.7",
|
||||
"license": "Apache-2.0"
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
'use strict';
|
||||
|
||||
const Changeset = require('./Changeset');
|
||||
const ChangesetUtils = require('./ChangesetUtils');
|
||||
const _ = require('./underscore');
|
||||
|
@ -72,10 +74,12 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({
|
|||
|
||||
const rowChangeset = this._setAttributesOnRangeByLine(row, startCol, endCol, attribs);
|
||||
|
||||
// compose changesets of all rows into a single changeset, as the range might not be continuous
|
||||
// compose changesets of all rows into a single changeset
|
||||
// as the range might not be continuous
|
||||
// due to the presence of line markers on the rows
|
||||
if (allChangesets) {
|
||||
allChangesets = Changeset.compose(allChangesets.toString(), rowChangeset.toString(), this.rep.apool);
|
||||
allChangesets = Changeset.compose(
|
||||
allChangesets.toString(), rowChangeset.toString(), this.rep.apool);
|
||||
} else {
|
||||
allChangesets = rowChangeset;
|
||||
}
|
||||
|
@ -118,7 +122,8 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({
|
|||
_setAttributesOnRangeByLine(row, startCol, endCol, attribs) {
|
||||
const builder = Changeset.builder(this.rep.lines.totalWidth());
|
||||
ChangesetUtils.buildKeepToStartOfRange(this.rep, builder, [row, startCol]);
|
||||
ChangesetUtils.buildKeepRange(this.rep, builder, [row, startCol], [row, endCol], attribs, this.rep.apool);
|
||||
ChangesetUtils.buildKeepRange(
|
||||
this.rep, builder, [row, startCol], [row, endCol], attribs, this.rep.apool);
|
||||
return builder;
|
||||
},
|
||||
|
||||
|
@ -127,9 +132,8 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({
|
|||
@param lineNum: the number of the line
|
||||
*/
|
||||
lineHasMarker(lineNum) {
|
||||
const that = this;
|
||||
|
||||
return _.find(lineAttributes, (attribute) => that.getAttributeOnLine(lineNum, attribute) != '') !== undefined;
|
||||
return lineAttributes.find(
|
||||
(attribute) => this.getAttributeOnLine(lineNum, attribute) !== '') !== undefined;
|
||||
},
|
||||
|
||||
/*
|
||||
|
@ -184,7 +188,7 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({
|
|||
if (!(rep.selStart && rep.selEnd)) return;
|
||||
// If we're looking for the caret attribute not the selection
|
||||
// has the user already got a selection or is this purely a caret location?
|
||||
const isNotSelection = (rep.selStart[0] == rep.selEnd[0] && rep.selEnd[1] === rep.selStart[1]);
|
||||
const isNotSelection = (rep.selStart[0] === rep.selEnd[0] && rep.selEnd[1] === rep.selStart[1]);
|
||||
if (isNotSelection) {
|
||||
if (prevChar) {
|
||||
// If it's not the start of the line
|
||||
|
@ -198,21 +202,18 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({
|
|||
[attributeName, 'true'],
|
||||
], rep.apool);
|
||||
const withItRegex = new RegExp(`${withIt.replace(/\*/g, '\\*')}(\\*|$)`);
|
||||
function hasIt(attribs) {
|
||||
return withItRegex.test(attribs);
|
||||
}
|
||||
const hasIt = (attribs) => withItRegex.test(attribs);
|
||||
|
||||
return rangeHasAttrib(rep.selStart, rep.selEnd);
|
||||
|
||||
function rangeHasAttrib(selStart, selEnd) {
|
||||
const rangeHasAttrib = (selStart, selEnd) => {
|
||||
// if range is collapsed -> no attribs in range
|
||||
if (selStart[1] == selEnd[1] && selStart[0] == selEnd[0]) return false;
|
||||
if (selStart[1] === selEnd[1] && selStart[0] === selEnd[0]) return false;
|
||||
|
||||
if (selStart[0] != selEnd[0]) { // -> More than one line selected
|
||||
var hasAttrib = true;
|
||||
if (selStart[0] !== selEnd[0]) { // -> More than one line selected
|
||||
let hasAttrib = true;
|
||||
|
||||
// from selStart to the end of the first line
|
||||
hasAttrib = hasAttrib && rangeHasAttrib(selStart, [selStart[0], rep.lines.atIndex(selStart[0]).text.length]);
|
||||
hasAttrib = hasAttrib && rangeHasAttrib(
|
||||
selStart, [selStart[0], rep.lines.atIndex(selStart[0]).text.length]);
|
||||
|
||||
// for all lines in between
|
||||
for (let n = selStart[0] + 1; n < selEnd[0]; n++) {
|
||||
|
@ -230,7 +231,7 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({
|
|||
const lineNum = selStart[0];
|
||||
const start = selStart[1];
|
||||
const end = selEnd[1];
|
||||
var hasAttrib = true;
|
||||
let hasAttrib = true;
|
||||
|
||||
// Iterate over attribs on this line
|
||||
|
||||
|
@ -244,7 +245,8 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({
|
|||
if (!hasIt(op.attribs)) {
|
||||
// does op overlap selection?
|
||||
if (!(opEndInLine <= start || opStartInLine >= end)) {
|
||||
hasAttrib = false; // since it's overlapping but hasn't got the attrib -> range hasn't got it
|
||||
// since it's overlapping but hasn't got the attrib -> range hasn't got it
|
||||
hasAttrib = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -252,7 +254,8 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({
|
|||
}
|
||||
|
||||
return hasAttrib;
|
||||
}
|
||||
};
|
||||
return rangeHasAttrib(rep.selStart, rep.selEnd);
|
||||
},
|
||||
|
||||
/*
|
||||
|
@ -349,13 +352,13 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({
|
|||
const hasMarker = this.lineHasMarker(lineNum);
|
||||
let found = false;
|
||||
|
||||
const attribs = _(this.getAttributesOnLine(lineNum)).map(function (attrib) {
|
||||
const attribs = this.getAttributesOnLine(lineNum).map((attrib) => {
|
||||
if (attrib[0] === attributeName && (!attributeValue || attrib[0] === attributeValue)) {
|
||||
found = true;
|
||||
return [attributeName, ''];
|
||||
return [attrib[0], ''];
|
||||
} else if (attrib[0] === 'author') {
|
||||
// update last author to make changes to line attributes on this line
|
||||
return [attributeName, this.author];
|
||||
return [attrib[0], this.author];
|
||||
}
|
||||
return attrib;
|
||||
});
|
||||
|
@ -373,7 +376,8 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({
|
|||
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);
|
||||
ChangesetUtils.buildKeepRange(
|
||||
this.rep, builder, [lineNum, 0], [lineNum, 1], attribs, this.rep.apool);
|
||||
}
|
||||
|
||||
return this.applyChangeset(builder);
|
||||
|
@ -394,7 +398,9 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({
|
|||
},
|
||||
|
||||
hasAttributeOnSelectionOrCaretPosition(attributeName) {
|
||||
const hasSelection = ((this.rep.selStart[0] !== this.rep.selEnd[0]) || (this.rep.selEnd[1] !== this.rep.selStart[1]));
|
||||
const hasSelection = (
|
||||
(this.rep.selStart[0] !== this.rep.selEnd[0]) || (this.rep.selEnd[1] !== this.rep.selStart[1])
|
||||
);
|
||||
let hasAttrib;
|
||||
if (hasSelection) {
|
||||
hasAttrib = this.getAttributeOnSelection(attributeName);
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* This code is mostly from the old Etherpad. Please help us to comment this code.
|
||||
* This helps other people to understand this code better and helps them to improve it.
|
||||
|
@ -22,33 +24,23 @@
|
|||
|
||||
const Security = require('./security');
|
||||
|
||||
function isNodeText(node) {
|
||||
return (node.nodeType == 3);
|
||||
}
|
||||
const isNodeText = (node) => (node.nodeType === 3);
|
||||
|
||||
function object(o) {
|
||||
const f = function () {};
|
||||
f.prototype = o;
|
||||
return new f();
|
||||
}
|
||||
const getAssoc = (obj, name) => obj[`_magicdom_${name}`];
|
||||
|
||||
function getAssoc(obj, name) {
|
||||
return obj[`_magicdom_${name}`];
|
||||
}
|
||||
|
||||
function setAssoc(obj, name, value) {
|
||||
const setAssoc = (obj, name, value) => {
|
||||
// note that in IE designMode, properties of a node can get
|
||||
// copied to new nodes that are spawned during editing; also,
|
||||
// properties representable in HTML text can survive copy-and-paste
|
||||
obj[`_magicdom_${name}`] = value;
|
||||
}
|
||||
};
|
||||
|
||||
// "func" is a function over 0..(numItems-1) that is monotonically
|
||||
// "increasing" with index (false, then true). Finds the boundary
|
||||
// between false and true, a number between 0 and numItems inclusive.
|
||||
|
||||
|
||||
function binarySearch(numItems, func) {
|
||||
const binarySearch = (numItems, func) => {
|
||||
if (numItems < 1) return 0;
|
||||
if (func(0)) return 0;
|
||||
if (!func(numItems - 1)) return numItems;
|
||||
|
@ -60,22 +52,19 @@ function binarySearch(numItems, func) {
|
|||
else low = x;
|
||||
}
|
||||
return high;
|
||||
}
|
||||
};
|
||||
|
||||
function binarySearchInfinite(expectedLength, func) {
|
||||
const binarySearchInfinite = (expectedLength, func) => {
|
||||
let i = 0;
|
||||
while (!func(i)) i += expectedLength;
|
||||
return binarySearch(i, func);
|
||||
}
|
||||
};
|
||||
|
||||
function htmlPrettyEscape(str) {
|
||||
return Security.escapeHTML(str).replace(/\r?\n/g, '\\n');
|
||||
}
|
||||
const htmlPrettyEscape = (str) => Security.escapeHTML(str).replace(/\r?\n/g, '\\n');
|
||||
|
||||
const noop = function () {};
|
||||
const noop = () => {};
|
||||
|
||||
exports.isNodeText = isNodeText;
|
||||
exports.object = object;
|
||||
exports.getAssoc = getAssoc;
|
||||
exports.setAssoc = setAssoc;
|
||||
exports.binarySearch = binarySearch;
|
||||
|
|
|
@ -113,7 +113,7 @@ const reconnectionTries = {
|
|||
|
||||
nextTry() {
|
||||
// double the time to try to reconnect on every time reconnection fails
|
||||
const nextCounterFactor = Math.pow(2, this.counter);
|
||||
const nextCounterFactor = 2 ** this.counter;
|
||||
this.counter++;
|
||||
|
||||
return nextCounterFactor;
|
||||
|
|
|
@ -55,7 +55,7 @@ const tsort = (edges) => {
|
|||
Object.keys(nodes).forEach(visit);
|
||||
|
||||
return sorted;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* TEST
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue