From 5d416579ee3a0943ee258625cc0de4c7a56e49e3 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 22 Jan 2013 22:33:51 +0000 Subject: [PATCH 01/22] bring in some padDiff stuff that doesnt suck --- src/node/db/API.js | 81 ++++++++++++++++++++++++++++++++++ src/node/handler/APIHandler.js | 38 ++++++++++++++++ src/node/utils/padDiff.js | 72 ++++++++++++++++++++++++++++++ 3 files changed, 191 insertions(+) create mode 100644 src/node/utils/padDiff.js diff --git a/src/node/db/API.js b/src/node/db/API.js index 9cad415d0..9cecdbd3b 100644 --- a/src/node/db/API.js +++ b/src/node/db/API.js @@ -568,6 +568,87 @@ exports.checkToken = function(callback) } +/** +createDiff(padID, startRev, endRev) returns an object of diffs from 2 points in a pad + +Example returns: + +TODO {"code":0,"message":"ok","data":null} +TODO {"code":4,"message":"no or wrong API Key","data":null} +*/ +exports.createDiff = function(padID, startRev, endRev, callback){ + //check if rev is a number + if(startRev !== undefined && typeof startRev != "number") + { + //try to parse the number + if(!isNaN(parseInt(startRev))) + { + startRev = parseInt(startRev, 10); + } + else + { + callback({stop: "startRev is not a number"}); + return; + } + } + + //check if rev is a number + if(endRev !== undefined && typeof endRev != "number") + { + //try to parse the number + if(!isNaN(parseInt(endRev))) + { + endRev = parseInt(endRev, 10); + } + else + { + callback({stop: "endRev is not a number"}); + return; + } + } + + //get the pad + getPadSafe(padID, true, function(err, pad) + { + if(err){ + return callback(err); + } + + try { + var padDiff = new PadDiff(pad, startRev, endRev); + } catch(e) { + return callback({stop:e.message}); + } + + var html, authors; + + async.series([ + function(callback){ + padDiff.getHtml(function(err, _html){ + if(err){ + return callback(err); + } + + html = _html; + callback(); + }); + }, + function(callback){ + padDiff.getAuthors(function(err, _authors){ + if(err){ + return callback(err); + } + + authors = _authors; + callback(); + }); + } + ], function(err){ + callback(err, {html: html, authors: authors}) + }); + }); +} + /******************************/ /** INTERNAL HELPER FUNCTIONS */ /******************************/ diff --git a/src/node/handler/APIHandler.js b/src/node/handler/APIHandler.js index ae93e933f..ec7e9f8d0 100644 --- a/src/node/handler/APIHandler.js +++ b/src/node/handler/APIHandler.js @@ -24,6 +24,7 @@ var fs = require("fs"); var api = require("../db/API"); var padManager = require("../db/PadManager"); var randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString; +var padDiff = require("../utils/padDiff"); //ensure we have an apikey var apikey = null; @@ -174,6 +175,43 @@ var version = , "listAllGroups" : [] , "checkToken" : [] } +, "1.2.2": + { "createGroup" : [] + , "createGroupIfNotExistsFor" : ["groupMapper"] + , "deleteGroup" : ["groupID"] + , "listPads" : ["groupID"] + , "listAllPads" : [] + , "createDiff" : ["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"] + , "getRevisionsCount" : ["padID"] + , "getLastEdited" : ["padID"] + , "deletePad" : ["padID"] + , "getReadOnlyID" : ["padID"] + , "setPublicStatus" : ["padID", "publicStatus"] + , "getPublicStatus" : ["padID"] + , "setPassword" : ["padID", "password"] + , "isPasswordProtected" : ["padID"] + , "listAuthorsOfPad" : ["padID"] + , "padUsersCount" : ["padID"] + , "getAuthorName" : ["authorID"] + , "padUsers" : ["padID"] + , "sendClientsMessage" : ["padID", "msg"] + , "listAllGroups" : [] + , "checkToken" : [] + } }; /** diff --git a/src/node/utils/padDiff.js b/src/node/utils/padDiff.js new file mode 100644 index 000000000..15957b823 --- /dev/null +++ b/src/node/utils/padDiff.js @@ -0,0 +1,72 @@ +exports.createDiff = function(padID, startRev, endRev, callback){ + //check if rev is a number + if(startRev !== undefined && typeof startRev != "number") + { + //try to parse the number + if(!isNaN(parseInt(startRev))) + { + startRev = parseInt(startRev, 10); + } + else + { + callback({stop: "startRev is not a number"}); + return; + } + } + + //check if rev is a number + if(endRev !== undefined && typeof endRev != "number") + { + //try to parse the number + if(!isNaN(parseInt(endRev))) + { + endRev = parseInt(endRev, 10); + } + else + { + callback({stop: "endRev is not a number"}); + return; + } + } + + //get the pad + getPadSafe(padID, true, function(err, pad) + { + if(err){ + return callback(err); + } + + try { + var padDiff = new PadDiff(pad, startRev, endRev); + } catch(e) { + return callback({stop:e.message}); + } + + var html, authors; + + async.series([ + function(callback){ + padDiff.getHtml(function(err, _html){ + if(err){ + return callback(err); + } + + html = _html; + callback(); + }); + }, + function(callback){ + padDiff.getAuthors(function(err, _authors){ + if(err){ + return callback(err); + } + + authors = _authors; + callback(); + }); + } + ], function(err){ + callback(err, {html: html, authors: authors}) + }); + }); +} From 205d9832253b9d62e7726178e2d6c5a14c3f64ac Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 22 Jan 2013 22:48:05 +0000 Subject: [PATCH 02/22] make it crash --- src/node/db/API.js | 1 + src/node/handler/APIHandler.js | 1 - src/node/utils/padDiff.js | 1 + 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/node/db/API.js b/src/node/db/API.js index 9cecdbd3b..72ca16328 100644 --- a/src/node/db/API.js +++ b/src/node/db/API.js @@ -30,6 +30,7 @@ var async = require("async"); var exportHtml = require("../utils/ExportHtml"); var importHtml = require("../utils/ImportHtml"); var cleanText = require("./Pad").cleanText; +var padDiff = require("../utils/padDiff"); /**********************/ /**GROUP FUNCTIONS*****/ diff --git a/src/node/handler/APIHandler.js b/src/node/handler/APIHandler.js index ec7e9f8d0..31b4b187c 100644 --- a/src/node/handler/APIHandler.js +++ b/src/node/handler/APIHandler.js @@ -24,7 +24,6 @@ var fs = require("fs"); var api = require("../db/API"); var padManager = require("../db/PadManager"); var randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString; -var padDiff = require("../utils/padDiff"); //ensure we have an apikey var apikey = null; diff --git a/src/node/utils/padDiff.js b/src/node/utils/padDiff.js index 15957b823..230668eb3 100644 --- a/src/node/utils/padDiff.js +++ b/src/node/utils/padDiff.js @@ -1,4 +1,5 @@ exports.createDiff = function(padID, startRev, endRev, callback){ +console.warn("WTF"); //check if rev is a number if(startRev !== undefined && typeof startRev != "number") { From d21585b8807b831e51a689bab6769d0ebfc1544b Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 22 Jan 2013 23:06:52 +0000 Subject: [PATCH 03/22] mheh --- src/node/db/API.js | 8 ++++++-- src/node/utils/padDiff.js | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/node/db/API.js b/src/node/db/API.js index 72ca16328..50c4a6c5d 100644 --- a/src/node/db/API.js +++ b/src/node/db/API.js @@ -30,7 +30,7 @@ var async = require("async"); var exportHtml = require("../utils/ExportHtml"); var importHtml = require("../utils/ImportHtml"); var cleanText = require("./Pad").cleanText; -var padDiff = require("../utils/padDiff"); +var PadDiff = require("../utils/padDiff"); /**********************/ /**GROUP FUNCTIONS*****/ @@ -611,16 +611,19 @@ exports.createDiff = function(padID, startRev, endRev, callback){ //get the pad getPadSafe(padID, true, function(err, pad) { +console.warn(padID); if(err){ return callback(err); } try { +console.warn(pad); var padDiff = new PadDiff(pad, startRev, endRev); +console.warn("AFTER"); } catch(e) { return callback({stop:e.message}); } - +/* var html, authors; async.series([ @@ -647,6 +650,7 @@ exports.createDiff = function(padID, startRev, endRev, callback){ ], function(err){ callback(err, {html: html, authors: authors}) }); + */ }); } diff --git a/src/node/utils/padDiff.js b/src/node/utils/padDiff.js index 230668eb3..645c2e2d4 100644 --- a/src/node/utils/padDiff.js +++ b/src/node/utils/padDiff.js @@ -1,5 +1,6 @@ exports.createDiff = function(padID, startRev, endRev, callback){ console.warn("WTF"); + //check if rev is a number if(startRev !== undefined && typeof startRev != "number") { From 3fb2f02875bfd0da7097a3fa021c7959bde1811b Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 22 Jan 2013 23:16:49 +0000 Subject: [PATCH 04/22] semi working --- src/node/db/API.js | 6 +- src/node/utils/padDiff.js | 596 ++++++++++++++++++++++++++++++++++---- 2 files changed, 539 insertions(+), 63 deletions(-) diff --git a/src/node/db/API.js b/src/node/db/API.js index 50c4a6c5d..2c8b91abb 100644 --- a/src/node/db/API.js +++ b/src/node/db/API.js @@ -611,19 +611,16 @@ exports.createDiff = function(padID, startRev, endRev, callback){ //get the pad getPadSafe(padID, true, function(err, pad) { -console.warn(padID); if(err){ return callback(err); } try { -console.warn(pad); var padDiff = new PadDiff(pad, startRev, endRev); -console.warn("AFTER"); } catch(e) { + // console.warn(e.stack); return callback({stop:e.message}); } -/* var html, authors; async.series([ @@ -650,7 +647,6 @@ console.warn("AFTER"); ], function(err){ callback(err, {html: html, authors: authors}) }); - */ }); } diff --git a/src/node/utils/padDiff.js b/src/node/utils/padDiff.js index 645c2e2d4..f898cbe16 100644 --- a/src/node/utils/padDiff.js +++ b/src/node/utils/padDiff.js @@ -1,74 +1,554 @@ -exports.createDiff = function(padID, startRev, endRev, callback){ -console.warn("WTF"); - - //check if rev is a number - if(startRev !== undefined && typeof startRev != "number") +var Changeset = require("../../static/js/Changeset"); +var async = require("async"); +var exportHtml = require('./ExportHtml'); + +function PadDiff (pad, fromRev, toRev){ + //check parameters + if(!pad || !pad.id || !pad.atext || !pad.pool) { - //try to parse the number - if(!isNaN(parseInt(startRev))) - { - startRev = parseInt(startRev, 10); - } - else - { - callback({stop: "startRev is not a number"}); - return; - } + throw new Error('Invalid pad'); } - //check if rev is a number - if(endRev !== undefined && typeof endRev != "number") - { - //try to parse the number - if(!isNaN(parseInt(endRev))) - { - endRev = parseInt(endRev, 10); - } - else - { - callback({stop: "endRev is not a number"}); - return; - } + var range = pad.getValidRevisionRange(fromRev, toRev); + if(!range) { throw new Error('Invalid revision range.' + + ' startRev: ' + fromRev + + ' endRev: ' + toRev); } + + this._pad = pad; + this._fromRev = range.startRev; + this._toRev = range.endRev; + this._html = null; + this._authors = []; +} + +PadDiff.prototype._isClearAuthorship = function(changeset){ + //unpack + var unpacked = Changeset.unpack(changeset); + + //check if there is nothing in the charBank + if(unpacked.charBank !== "") + return false; + + //check if oldLength == newLength + if(unpacked.oldLen !== unpacked.newLen) + return false; + + //lets iterator over the operators + var iterator = Changeset.opIterator(unpacked.ops); + + //get the first operator, this should be a clear operator + var clearOperator = iterator.next(); + + //check if there is only one operator + if(iterator.hasNext() === true) + return false; + + //check if this operator doesn't change text + if(clearOperator.opcode !== "=") + return false; + + //check that this operator applys to the complete text + //if the text ends with a new line, its exactly one character less, else it has the same length + if(clearOperator.chars !== unpacked.oldLen-1 && clearOperator.chars !== unpacked.oldLen) + return false; + + var attributes = []; + Changeset.eachAttribNumber(changeset, function(attrNum){ + attributes.push(attrNum); + }); + + //check that this changeset uses only one attribute + if(attributes.length !== 1) + return false; + + var appliedAttribute = this._pad.pool.getAttrib(attributes[0]); + + //check if the applied attribute is an anonymous author attribute + if(appliedAttribute[0] !== "author" || appliedAttribute[1] !== "") + return false; + + return true; +} + +PadDiff.prototype._createClearAuthorship = function(rev, callback){ + var self = this; + this._pad.getInternalRevisionAText(rev, function(err, atext){ + if(err){ + return callback(err); + } + + //build clearAuthorship changeset + var builder = Changeset.builder(atext.text.length); + builder.keepText(atext.text, [['author','']], self._pad.pool); + var changeset = builder.toString(); + + callback(null, changeset); + }); +} + +PadDiff.prototype._createClearStartAtext = function(rev, callback){ + var self = this; + + //get the atext of this revision + this._pad.getInternalRevisionAText(rev, function(err, atext){ + if(err){ + return callback(err); + } + + //create the clearAuthorship changeset + self._createClearAuthorship(rev, function(err, changeset){ + if(err){ + return callback(err); + } + + //apply the clearAuthorship changeset + var newAText = Changeset.applyToAText(changeset, atext, self._pad.pool); + + callback(null, newAText); + }); + }); +} + +PadDiff.prototype._getChangesetsInBulk = function(startRev, count, callback) { + var self = this; + + //find out which revisions we need + var revisions = []; + for(var i=startRev;i<(startRev+count) && i<=this._pad.head;i++){ + revisions.push(i); } + + var changesets = [], authors = []; + + //get all needed revisions + async.forEach(revisions, function(rev, callback){ + self._pad.getRevision(rev, function(err, revision){ + if(err){ + return callback(err) + } + + var arrayNum = rev-startRev; + + changesets[arrayNum] = revision.changeset; + authors[arrayNum] = revision.meta.author; + + callback(); + }); + }, function(err){ + callback(err, changesets, authors); + }); +} - //get the pad - getPadSafe(padID, true, function(err, pad) - { - if(err){ - return callback(err); +PadDiff.prototype._addAuthors = function(authors) { + var self = this; + //add to array if not in the array + authors.forEach(function(author){ + if(self._authors.indexOf(author) == -1){ + self._authors.push(author); } + }); +} - try { - var padDiff = new PadDiff(pad, startRev, endRev); - } catch(e) { - return callback({stop:e.message}); - } +PadDiff.prototype._createDiffAtext = function(callback) { + var self = this; + var bulkSize = 100; + + //get the cleaned startAText + self._createClearStartAtext(self._fromRev, function(err, atext){ + if(err) { return callback(err); } + + var superChangeset = null; + + var rev = self._fromRev + 1; - var html, authors; - - async.series([ - function(callback){ - padDiff.getHtml(function(err, _html){ - if(err){ - return callback(err); + //async while loop + async.whilst( + //loop condition + function () { return rev <= self._toRev; }, + + //loop body + function (callback) { + //get the bulk + self._getChangesetsInBulk(rev,bulkSize,function(err, changesets, authors){ + var addedAuthors = []; + + //run trough all changesets + for(var i=0;i= curChar) { + curLineNextOp.chars -= (curChar - indexIntoLine); + done = true; + } else { + indexIntoLine += curLineNextOp.chars; + } + } + } + + while (numChars > 0) { + if ((!curLineNextOp.chars) && (!curLineOpIter.hasNext())) { + curLine++; + curChar = 0; + curLineOpIterLine = curLine; + curLineNextOp.chars = 0; + curLineOpIter = Changeset.opIterator(alines_get(curLine)); + } + if (!curLineNextOp.chars) { + curLineOpIter.next(curLineNextOp); + } + var charsToUse = Math.min(numChars, curLineNextOp.chars); + func(charsToUse, curLineNextOp.attribs, charsToUse == curLineNextOp.chars && curLineNextOp.lines > 0); + numChars -= charsToUse; + curLineNextOp.chars -= charsToUse; + curChar += charsToUse; + } + + if ((!curLineNextOp.chars) && (!curLineOpIter.hasNext())) { + curLine++; + curChar = 0; + } + } + + function skip(N, L) { + if (L) { + curLine += L; + curChar = 0; + } else { + if (curLineOpIter && curLineOpIterLine == curLine) { + consumeAttribRuns(N, function () {}); + } else { + curChar += N; + } + } + } + + function nextText(numChars) { + var len = 0; + var assem = Changeset.stringAssembler(); + var firstString = lines_get(curLine).substring(curChar); + len += firstString.length; + assem.append(firstString); + + var lineNum = curLine + 1; + while (len < numChars) { + var nextString = lines_get(lineNum); + len += nextString.length; + assem.append(nextString); + lineNum++; + } + + return assem.toString().substring(0, numChars); + } + + function cachedStrFunc(func) { + var cache = {}; + return function (s) { + if (!cache[s]) { + cache[s] = func(s); + } + return cache[s]; + }; + } + + var attribKeys = []; + var attribValues = []; + + //iterate over all operators of this changeset + while (csIter.hasNext()) { + var csOp = csIter.next(); + + if (csOp.opcode == '=') { + var textBank = nextText(csOp.chars); + + // decide if this equal operator is an attribution change or not. We can see this by checkinf if attribs is set. + // If the text this operator applies to is only a star, than this is a false positive and should be ignored + if (csOp.attribs && textBank != "*") { + var deletedAttrib = apool.putAttrib(["removed", true]); + var authorAttrib = apool.putAttrib(["author", ""]);; + + attribKeys.length = 0; + attribValues.length = 0; + Changeset.eachAttribNumber(csOp.attribs, function (n) { + attribKeys.push(apool.getAttribKey(n)); + attribValues.push(apool.getAttribValue(n)); + + if(apool.getAttribKey(n) === "author"){ + authorAttrib = n; + }; + }); + + var undoBackToAttribs = cachedStrFunc(function (attribs) { + var backAttribs = []; + for (var i = 0; i < attribKeys.length; i++) { + var appliedKey = attribKeys[i]; + var appliedValue = attribValues[i]; + var oldValue = Changeset.attribsAttributeValue(attribs, appliedKey, apool); + if (appliedValue != oldValue) { + backAttribs.push([appliedKey, oldValue]); + } + } + return Changeset.makeAttribsString('=', backAttribs, apool); + }); + + var oldAttribsAddition = "*" + Changeset.numToString(deletedAttrib) + "*" + Changeset.numToString(authorAttrib); + + var textLeftToProcess = textBank; + + while(textLeftToProcess.length > 0){ + //process till the next line break or process only one line break + var lengthToProcess = textLeftToProcess.indexOf("\n"); + var lineBreak = false; + switch(lengthToProcess){ + case -1: + lengthToProcess=textLeftToProcess.length; + break; + case 0: + lineBreak = true; + lengthToProcess=1; + break; + } + + //get the text we want to procceed in this step + var processText = textLeftToProcess.substr(0, lengthToProcess); + textLeftToProcess = textLeftToProcess.substr(lengthToProcess); + + if(lineBreak){ + builder.keep(1, 1); //just skip linebreaks, don't do a insert + keep for a linebreak + + //consume the attributes of this linebreak + consumeAttribRuns(1, function(){}); + } else { + //add the old text via an insert, but add a deletion attribute + the author attribute of the author who deleted it + var textBankIndex = 0; + consumeAttribRuns(lengthToProcess, function (len, attribs, endsLine) { + //get the old attributes back + var attribs = (undoBackToAttribs(attribs) || "") + oldAttribsAddition; + + builder.insert(processText.substr(textBankIndex, len), attribs); + textBankIndex += len; + }); + + builder.keep(lengthToProcess, 0); + } + } + } else { + skip(csOp.chars, csOp.lines); + builder.keep(csOp.chars, csOp.lines); + } + } else if (csOp.opcode == '+') { + builder.keep(csOp.chars, csOp.lines); + } else if (csOp.opcode == '-') { + var textBank = nextText(csOp.chars); + var textBankIndex = 0; + + consumeAttribRuns(csOp.chars, function (len, attribs, endsLine) { + builder.insert(textBank.substr(textBankIndex, len), attribs + csOp.attribs); + textBankIndex += len; + }); + } + } + + return Changeset.checkRep(builder.toString()); +}; + +//export the constructor +module.exports = PadDiff; From f1b9c213eef910f7f4b94e40b526206aa9763b35 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 22 Jan 2013 23:37:53 +0000 Subject: [PATCH 05/22] and semi working --- src/node/db/Pad.js | 41 ++++++++++++++++++++++++++++++++++++ src/node/utils/ExportHtml.js | 2 +- src/node/utils/padDiff.js | 7 ++++-- 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/node/db/Pad.js b/src/node/db/Pad.js index da1ce9e16..037886eac 100644 --- a/src/node/db/Pad.js +++ b/src/node/db/Pad.js @@ -213,6 +213,47 @@ Pad.prototype.getInternalRevisionAText = function getInternalRevisionAText(targe }); }; +Pad.prototype.getRevision = function getRevisionChangeset(revNum, callback) { + db.get("pad:"+this.id+":revs:"+revNum, callback); +}; + +Pad.prototype.getAllAuthorColors = function getAllAuthorColors(callback){ + var authors = this.getAllAuthors(); + var returnTable = {}; + var colorPalette = authorManager.getColorPalette(); + + async.forEach(authors, function(author, callback){ + authorManager.getAuthorColorId(author, function(err, colorId){ + if(err){ + return callback(err); + } + returnTable[author]=colorPalette[colorId]; + + callback(); + }); + }, function(err){ + callback(err, returnTable); + }); +}; + +Pad.prototype.getValidRevisionRange = function getValidRevisionRange(startRev, endRev) { + startRev = parseInt(startRev, 10); + var head = this.getHeadRevisionNumber(); + endRev = endRev ? parseInt(endRev, 10) : head; + if(isNaN(startRev) || startRev < 0 || startRev > head) { + startRev = null; + } + if(isNaN(endRev) || endRev < startRev) { + endRev = null; + } else if(endRev > head) { + endRev = head; + } + if(startRev !== null && endRev !== null) { + return { startRev: startRev , endRev: endRev } + } + return null; +}; + Pad.prototype.getKeyRevisionNumber = function getKeyRevisionNumber(revNum) { return Math.floor(revNum / 100) * 100; }; diff --git a/src/node/utils/ExportHtml.js b/src/node/utils/ExportHtml.js index 354030133..f385d4704 100644 --- a/src/node/utils/ExportHtml.js +++ b/src/node/utils/ExportHtml.js @@ -92,7 +92,7 @@ function getPadHTML(pad, revNum, callback) exports.getPadHTML = getPadHTML; -function getHTMLFromAtext(pad, atext) +exports.getHTMLFromAtext = function(pad, atext) { var apool = pad.apool(); var textLines = atext.text.slice(0, -1).split('\n'); diff --git a/src/node/utils/padDiff.js b/src/node/utils/padDiff.js index f898cbe16..b1fa9277a 100644 --- a/src/node/utils/padDiff.js +++ b/src/node/utils/padDiff.js @@ -244,14 +244,17 @@ PadDiff.prototype.getHtml = function(callback){ }, //get the authorColor table function(callback){ - self._pad.getAllAuthorColors(function(err, _authorColors){ + /* + self._pad.getAllAuthorColors(function(err, _authorColors){ // TODO if(err){ return callback(err); } authorColors = _authorColors; - callback(); }); + */ + authorColors = {}; + callback(); }, //convert the atext to html function(callback){ From 77403942eebdf9f6f103a858ceec72899d68574b Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 27 Jan 2013 03:08:21 +0000 Subject: [PATCH 06/22] fix make file issue --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 4e870a452..c63bb0bfb 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 $< > $@ - cat $@ | sed 's/__VERSION__/${VERSION}/' > $@ + sed -i 's/__VERSION__/${VERSION}/' $@ clean: rm -rf out/ From 7432b7aff94775419333c955044e62635cabd6b6 Mon Sep 17 00:00:00 2001 From: mluto Date: Sun, 27 Jan 2013 11:02:15 +0100 Subject: [PATCH 07/22] Rewrote getParams() to be more dynamic and shorter --- src/static/js/pad.js | 103 ++++++++++++------------------------------- 1 file changed, 28 insertions(+), 75 deletions(-) diff --git a/src/static/js/pad.js b/src/static/js/pad.js index 64d8b42b8..9f0fba7fa 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.js @@ -101,86 +101,39 @@ function randomString() return "t." + randomstring; } +// This array represents all GET-parameters which can be used to change a setting. +// name: the parameter-name, eg `?noColors=true` => `noColors` +// checkVal: the callback is only executed when +// * the parameter was supplied and matches checkVal +// * the parameter was supplied and checkVal is null +// callback: the function to call when all above succeeds, `val` is the value supplied by the user +var paramSettings = [ + { 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: "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; } }, + // If the username is set as a parameter we should set a global value that we can call once we have initiated the pad. + { name: "userName", checkVal: null, callback: function(val) { settings.globalUserName = decodeURIComponent(val); } }, + // If the userColor is set as a parameter, set a global value to use once we have initiated the pad. + { 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: "lang", checkVal: null, callback: function(val) { window.html10n.localize([val, 'en']); } } +]; + function getParams() { var params = getUrlVars() - var showControls = params["showControls"]; - var showChat = params["showChat"]; - var userName = params["userName"]; - var userColor = params["userColor"]; - var showLineNumbers = params["showLineNumbers"]; - var useMonospaceFont = params["useMonospaceFont"]; - var IsnoColors = params["noColors"]; - var rtl = params["rtl"]; - var alwaysShowChat = params["alwaysShowChat"]; - var lang = params["lang"]; - - if(IsnoColors) + + for(var i = 0; i < paramSettings.length; i++) { - if(IsnoColors == "true") + var setting = paramSettings[i]; + var value = params[setting.name]; + + if(value && (value == setting.checkVal || setting.checkVal == null)) { - settings.noColors = true; - $('#clearAuthorship').hide(); - } - } - if(showControls) - { - if(showControls == "false") - { - $('#editbar').hide(); - $('#editorcontainer').css({"top":"0px"}); - } - } - if(showChat) - { - if(showChat == "false") - { - $('#chaticon').hide(); - } - } - if(showLineNumbers) - { - if(showLineNumbers == "false") - { - settings.LineNumbersDisabled = true; - } - } - if(useMonospaceFont) - { - if(useMonospaceFont == "true") - { - settings.useMonospaceFontGlobal = true; - } - } - if(userName) - { - // If the username is set as a parameter we should set a global value that we can call once we have initiated the pad. - settings.globalUserName = decodeURIComponent(userName); - } - if(userColor) - // If the userColor is set as a parameter, set a global value to use once we have initiated the pad. - { - settings.globalUserColor = decodeURIComponent(userColor); - } - if(rtl) - { - if(rtl == "true") - { - settings.rtlIsTrue = true - } - } - if(alwaysShowChat) - { - if(alwaysShowChat == "true") - { - chat.stickToScreen(); - } - } - if(lang) - { - if(lang !== "") - { - window.html10n.localize([lang, 'en']); + setting.callback(value); } } } From f4690dda9dec3ba9afce3f22b49f91a624d4fae4 Mon Sep 17 00:00:00 2001 From: Peter 'Pita' Martischka Date: Sun, 27 Jan 2013 15:40:05 +0000 Subject: [PATCH 08/22] fixed indent --- 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 31b4b187c..9d017b280 100644 --- a/src/node/handler/APIHandler.js +++ b/src/node/handler/APIHandler.js @@ -180,7 +180,7 @@ var version = , "deleteGroup" : ["groupID"] , "listPads" : ["groupID"] , "listAllPads" : [] - , "createDiff" : ["padID", "startRev", "endRev"] + , "createDiff" : ["padID", "startRev", "endRev"] , "createPad" : ["padID", "text"] , "createGroupPad" : ["groupID", "padName", "text"] , "createAuthor" : ["name"] From dcfb1b2ea4bd3ee8783c6a01aeebc8c760aa7a06 Mon Sep 17 00:00:00 2001 From: Peter 'Pita' Martischka Date: Sun, 27 Jan 2013 15:40:37 +0000 Subject: [PATCH 09/22] Added missing functions to create pad diffs --- src/static/js/Changeset.js | 118 +++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) diff --git a/src/static/js/Changeset.js b/src/static/js/Changeset.js index cfea43625..b16042126 100644 --- a/src/static/js/Changeset.js +++ b/src/static/js/Changeset.js @@ -2182,3 +2182,121 @@ exports.followAttributes = function (att1, att2, pool) { } return buf.toString(); }; + +exports.composeWithDeletions = function (cs1, cs2, pool) { + var unpacked1 = exports.unpack(cs1); + var unpacked2 = exports.unpack(cs2); + var len1 = unpacked1.oldLen; + var len2 = unpacked1.newLen; + exports.assert(len2 == unpacked2.oldLen, "mismatched composition"); + var len3 = unpacked2.newLen; + var bankIter1 = exports.stringIterator(unpacked1.charBank); + var bankIter2 = exports.stringIterator(unpacked2.charBank); + var bankAssem = exports.stringAssembler(); + + var newOps = exports.applyZip(unpacked1.ops, 0, unpacked2.ops, 0, function (op1, op2, opOut) { + var op1code = op1.opcode; + var op2code = op2.opcode; + if (op1code == '+' && op2code == '-') { + bankIter1.skip(Math.min(op1.chars, op2.chars)); + } + exports._slicerZipperFuncWithDeletions(op1, op2, opOut, pool); + if (opOut.opcode == '+') { + if (op2code == '+') { + bankAssem.append(bankIter2.take(opOut.chars)); + } else { + bankAssem.append(bankIter1.take(opOut.chars)); + } + } + }); + + return exports.pack(len1, len3, newOps, bankAssem.toString()); +}; + +// This function is 95% like _slicerZipperFunc, we just changed two lines to ensure it merges the attribs of deletions properly. +// This is necassary for correct paddiff. But to ensure these changes doesn't affect anything else, we've created a seperate function only used for paddiffs +exports._slicerZipperFuncWithDeletions= function (attOp, csOp, opOut, pool) { + // attOp is the op from the sequence that is being operated on, either an + // attribution string or the earlier of two exportss being composed. + // pool can be null if definitely not needed. + //print(csOp.toSource()+" "+attOp.toSource()+" "+opOut.toSource()); + if (attOp.opcode == '-') { + exports.copyOp(attOp, opOut); + attOp.opcode = ''; + } else if (!attOp.opcode) { + exports.copyOp(csOp, opOut); + csOp.opcode = ''; + } else { + switch (csOp.opcode) { + case '-': + { + if (csOp.chars <= attOp.chars) { + // delete or delete part + if (attOp.opcode == '=') { + opOut.opcode = '-'; + opOut.chars = csOp.chars; + opOut.lines = csOp.lines; + opOut.attribs = csOp.attribs; //changed by yammer + } + attOp.chars -= csOp.chars; + attOp.lines -= csOp.lines; + csOp.opcode = ''; + if (!attOp.chars) { + attOp.opcode = ''; + } + } else { + // delete and keep going + if (attOp.opcode == '=') { + opOut.opcode = '-'; + opOut.chars = attOp.chars; + opOut.lines = attOp.lines; + opOut.attribs = csOp.attribs; //changed by yammer + } + csOp.chars -= attOp.chars; + csOp.lines -= attOp.lines; + attOp.opcode = ''; + } + break; + } + case '+': + { + // insert + exports.copyOp(csOp, opOut); + csOp.opcode = ''; + break; + } + case '=': + { + if (csOp.chars <= attOp.chars) { + // keep or keep part + opOut.opcode = attOp.opcode; + opOut.chars = csOp.chars; + opOut.lines = csOp.lines; + opOut.attribs = exports.composeAttributes(attOp.attribs, csOp.attribs, attOp.opcode == '=', pool); + csOp.opcode = ''; + attOp.chars -= csOp.chars; + attOp.lines -= csOp.lines; + if (!attOp.chars) { + attOp.opcode = ''; + } + } else { + // keep and keep going + opOut.opcode = attOp.opcode; + opOut.chars = attOp.chars; + opOut.lines = attOp.lines; + opOut.attribs = exports.composeAttributes(attOp.attribs, csOp.attribs, attOp.opcode == '=', pool); + attOp.opcode = ''; + csOp.chars -= attOp.chars; + csOp.lines -= attOp.lines; + } + break; + } + case '': + { + exports.copyOp(attOp, opOut); + attOp.opcode = ''; + break; + } + } + } +}; From 6f743c6ee21dc96a286d2f52c3902bce11f7e19d Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 27 Jan 2013 16:45:01 +0000 Subject: [PATCH 10/22] make makefile work for darwin(mac) --- Makefile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c63bb0bfb..09dbf0f3e 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,7 @@ outdoc_files = $(addprefix out/,$(doc_sources:.md=.html)) docassets = $(addprefix out/,$(wildcard doc/assets/*)) VERSION = $(shell node -e "console.log( require('./src/package.json').version )") +UNAME := $(shell uname -s) docs: $(outdoc_files) $(docassets) @@ -14,7 +15,10 @@ out/doc/assets/%: doc/assets/% out/doc/%.html: doc/%.md mkdir -p $(@D) node tools/doc/generate.js --format=html --template=doc/template.html $< > $@ - sed -i 's/__VERSION__/${VERSION}/' $@ + ifeq ($(UNAME),Darwin) + sed -i '' 's/__VERSION__/${VERSION}/' $@ + else + sed -i 's/__VERSION__/${VERSION}/' $@ clean: rm -rf out/ From 07a267be7a372df4e2d7a899d4af5ba7b30057b8 Mon Sep 17 00:00:00 2001 From: Peter 'Pita' Martischka Date: Sun, 27 Jan 2013 16:45:09 +0000 Subject: [PATCH 11/22] Added colors to pad diff --- src/node/db/API.js | 1 - src/node/db/AuthorManager.js | 4 ++ src/node/db/Pad.js | 3 +- src/node/handler/PadMessageHandler.js | 2 +- src/node/utils/ExportHtml.js | 91 +++++++++++++++++++++++---- src/node/utils/padDiff.js | 9 +-- 6 files changed, 89 insertions(+), 21 deletions(-) diff --git a/src/node/db/API.js b/src/node/db/API.js index 2c8b91abb..ef341bef9 100644 --- a/src/node/db/API.js +++ b/src/node/db/API.js @@ -618,7 +618,6 @@ exports.createDiff = function(padID, startRev, endRev, callback){ try { var padDiff = new PadDiff(pad, startRev, endRev); } catch(e) { - // console.warn(e.stack); return callback({stop:e.message}); } var html, authors; diff --git a/src/node/db/AuthorManager.js b/src/node/db/AuthorManager.js index 28b2dd91e..667e0605d 100644 --- a/src/node/db/AuthorManager.js +++ b/src/node/db/AuthorManager.js @@ -24,6 +24,10 @@ var db = require("./DB").db; var async = require("async"); var randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString; +exports.getColorPalette = function(){ + return ["#ffc7c7", "#fff1c7", "#e3ffc7", "#c7ffd5", "#c7ffff", "#c7d5ff", "#e3c7ff", "#ffc7f1", "#ff8f8f", "#ffe38f", "#c7ff8f", "#8fffab", "#8fffff", "#8fabff", "#c78fff", "#ff8fe3", "#d97979", "#d9c179", "#a9d979", "#79d991", "#79d9d9", "#7991d9", "#a979d9", "#d979c1", "#d9a9a9", "#d9cda9", "#c1d9a9", "#a9d9b5", "#a9d9d9", "#a9b5d9", "#c1a9d9", "#d9a9cd", "#4c9c82", "#12d1ad", "#2d8e80", "#7485c3", "#a091c7", "#3185ab", "#6818b4", "#e6e76d", "#a42c64", "#f386e5", "#4ecc0c", "#c0c236", "#693224", "#b5de6a", "#9b88fd", "#358f9b", "#496d2f", "#e267fe", "#d23056", "#1a1a64", "#5aa335", "#d722bb", "#86dc6c", "#b5a714", "#955b6a", "#9f2985", "#4b81c8", "#3d6a5b", "#434e16", "#d16084", "#af6a0e", "#8c8bd8"]; +}; + /** * Checks if the author exists */ diff --git a/src/node/db/Pad.js b/src/node/db/Pad.js index 037886eac..4701e82a3 100644 --- a/src/node/db/Pad.js +++ b/src/node/db/Pad.js @@ -227,7 +227,8 @@ Pad.prototype.getAllAuthorColors = function getAllAuthorColors(callback){ if(err){ return callback(err); } - returnTable[author]=colorPalette[colorId]; + //colorId might be a hex color or an number out of the palette + returnTable[author]=colorPalette[colorId] || colorId; callback(); }); diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index 434c25ad6..24f72c796 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -1028,7 +1028,7 @@ function handleClientReady(client, message) "globalPadId": message.padId, "time": currentTime, }, - "colorPalette": ["#ffc7c7", "#fff1c7", "#e3ffc7", "#c7ffd5", "#c7ffff", "#c7d5ff", "#e3c7ff", "#ffc7f1", "#ff8f8f", "#ffe38f", "#c7ff8f", "#8fffab", "#8fffff", "#8fabff", "#c78fff", "#ff8fe3", "#d97979", "#d9c179", "#a9d979", "#79d991", "#79d9d9", "#7991d9", "#a979d9", "#d979c1", "#d9a9a9", "#d9cda9", "#c1d9a9", "#a9d9b5", "#a9d9d9", "#a9b5d9", "#c1a9d9", "#d9a9cd", "#4c9c82", "#12d1ad", "#2d8e80", "#7485c3", "#a091c7", "#3185ab", "#6818b4", "#e6e76d", "#a42c64", "#f386e5", "#4ecc0c", "#c0c236", "#693224", "#b5de6a", "#9b88fd", "#358f9b", "#496d2f", "#e267fe", "#d23056", "#1a1a64", "#5aa335", "#d722bb", "#86dc6c", "#b5a714", "#955b6a", "#9f2985", "#4b81c8", "#3d6a5b", "#434e16", "#d16084", "#af6a0e", "#8c8bd8"], + "colorPalette": authorManager.getColorPalette(), "clientIp": "127.0.0.1", "userIsGuest": true, "userColor": authorColorId, diff --git a/src/node/utils/ExportHtml.js b/src/node/utils/ExportHtml.js index f385d4704..d9ba3df48 100644 --- a/src/node/utils/ExportHtml.js +++ b/src/node/utils/ExportHtml.js @@ -1,12 +1,12 @@ /** * Copyright 2009 Google Inc. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS-IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -92,7 +92,7 @@ function getPadHTML(pad, revNum, callback) exports.getPadHTML = getPadHTML; -exports.getHTMLFromAtext = function(pad, atext) +exports.getHTMLFromAtext = function(pad, atext, authorColors) { var apool = pad.apool(); var textLines = atext.text.slice(0, -1).split('\n'); @@ -101,6 +101,42 @@ exports.getHTMLFromAtext = function(pad, atext) var tags = ['h1', 'h2', 'strong', 'em', 'u', 's']; var props = ['heading1', 'heading2', 'bold', 'italic', 'underline', 'strikethrough']; var anumMap = {}; + var css = ""; + + var stripDotFromAuthorID = function(id){ + return id.replace(/\./g,'_'); + }; + + if(authorColors){ + css+=""; + } props.forEach(function (propName, i) { @@ -125,22 +161,53 @@ exports.getHTMLFromAtext = function(pad, atext) // Just bold Bold and italics Just italics var taker = Changeset.stringIterator(text); var assem = Changeset.stringAssembler(); - var openTags = []; + + function getSpanClassFor(i){ + //return if author colors are disabled + if (!authorColors) return false; + + var property = props[i]; + + if(property.substr(0,6) === "author"){ + return stripDotFromAuthorID(property); + } + + if(property === "removed"){ + return "removed"; + } + + return false; + } + function emitOpenTag(i) { openTags.unshift(i); - assem.append('<'); - assem.append(tags[i]); - assem.append('>'); + var spanClass = getSpanClassFor(i); + + if(spanClass){ + assem.append(''); + } else { + assem.append('<'); + assem.append(tags[i]); + assem.append('>'); + } } function emitCloseTag(i) { openTags.shift(); - assem.append(''); + var spanClass = getSpanClassFor(i); + + if(spanClass){ + assem.append(''); + } else { + assem.append(''); + } } function orderdCloseTags(tags2close) @@ -303,7 +370,7 @@ exports.getHTMLFromAtext = function(pad, atext) return _processSpaces(assem.toString()); } // end getLineHTML - var pieces = []; + var pieces = [css]; // Need to deal with constraints imposed on HTML lists; can // only gain one level of nesting at once, can't change type diff --git a/src/node/utils/padDiff.js b/src/node/utils/padDiff.js index b1fa9277a..1b3cf58f5 100644 --- a/src/node/utils/padDiff.js +++ b/src/node/utils/padDiff.js @@ -244,17 +244,14 @@ PadDiff.prototype.getHtml = function(callback){ }, //get the authorColor table function(callback){ - /* - self._pad.getAllAuthorColors(function(err, _authorColors){ // TODO + self._pad.getAllAuthorColors(function(err, _authorColors){ if(err){ return callback(err); } authorColors = _authorColors; + callback(); }); - */ - authorColors = {}; - callback(); }, //convert the atext to html function(callback){ @@ -265,7 +262,7 @@ PadDiff.prototype.getHtml = function(callback){ ], function(err){ callback(err, html); }); -} +}; PadDiff.prototype.getAuthors = function(callback){ var self = this; From d478ae23863f85dd1918e4b9494a717592bd60fd Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 27 Jan 2013 16:59:52 +0000 Subject: [PATCH 12/22] take two at fixin Mac issue --- Makefile | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 09dbf0f3e..b656d5d9b 100644 --- a/Makefile +++ b/Makefile @@ -15,10 +15,11 @@ out/doc/assets/%: doc/assets/% out/doc/%.html: doc/%.md mkdir -p $(@D) node tools/doc/generate.js --format=html --template=doc/template.html $< > $@ - ifeq ($(UNAME),Darwin) - sed -i '' 's/__VERSION__/${VERSION}/' $@ - else - sed -i 's/__VERSION__/${VERSION}/' $@ +ifeq ($(UNAME),Darwin) + sed -i '' 's/__VERSION__/${VERSION}/' $@ +else + sed -i 's/__VERSION__/${VERSION}/' $@ +endif clean: rm -rf out/ From 878fd7631c2f4df82620fa3b8642ba856519d584 Mon Sep 17 00:00:00 2001 From: Peter 'Pita' Martischka Date: Sun, 27 Jan 2013 17:25:50 +0000 Subject: [PATCH 13/22] Fixed HTML export --- src/node/utils/ExportHtml.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/node/utils/ExportHtml.js b/src/node/utils/ExportHtml.js index d9ba3df48..069194880 100644 --- a/src/node/utils/ExportHtml.js +++ b/src/node/utils/ExportHtml.js @@ -91,8 +91,9 @@ function getPadHTML(pad, revNum, callback) } exports.getPadHTML = getPadHTML; +exports.getHTMLFromAtext = getHTMLFromAtext; -exports.getHTMLFromAtext = function(pad, atext, authorColors) +function getHTMLFromAtext(pad, atext, authorColors) { var apool = pad.apool(); var textLines = atext.text.slice(0, -1).split('\n'); From ed5644d4e5280c73db9a1862fd96dda3a5e8b132 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 27 Jan 2013 17:51:40 +0000 Subject: [PATCH 14/22] docs --- src/node/db/API.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/node/db/API.js b/src/node/db/API.js index ef341bef9..0eb404e49 100644 --- a/src/node/db/API.js +++ b/src/node/db/API.js @@ -574,8 +574,8 @@ createDiff(padID, startRev, endRev) returns an object of diffs from 2 points in Example returns: -TODO {"code":0,"message":"ok","data":null} -TODO {"code":4,"message":"no or wrong API Key","data":null} +{"code":0,"message":"ok","data":{"html":"Welcome to Etherpad Lite!

This pad text is synchronized as you type, so that everyone viewing this page sees the same text. This allows you to collaborate seamlessly on documents!

Get involved with Etherpad at http://etherpad.org
aw

","authors":["a.HKIv23mEbachFYfH",""]}} +{"code":4,"message":"no or wrong API Key","data":null} */ exports.createDiff = function(padID, startRev, endRev, callback){ //check if rev is a number From 75b861e93b4c6a6456fab4a824b14a0ae54e3b2c Mon Sep 17 00:00:00 2001 From: Siebrand Mazeland Date: Sun, 27 Jan 2013 19:41:00 +0000 Subject: [PATCH 15/22] Localisation updates from http://translatewiki.net. --- src/locales/ast.json | 3 +++ src/locales/az.json | 12 +++++++++- src/locales/da.json | 1 + src/locales/de.json | 2 ++ src/locales/es.json | 1 + src/locales/fi.json | 4 +++- src/locales/it.json | 1 + src/locales/ml.json | 16 ++++++------- src/locales/nl.json | 2 ++ src/locales/oc.json | 33 +++++++++++++++++++++---- src/locales/pl.json | 1 + src/locales/ps.json | 52 +++++++++++++++++++++++++++++++++++++++- src/locales/pt-br.json | 1 + src/locales/ru.json | 26 +++++++++++++++++++- src/locales/sl.json | 1 + src/locales/uk.json | 1 + src/locales/zh-hant.json | 1 + 17 files changed, 141 insertions(+), 17 deletions(-) diff --git a/src/locales/ast.json b/src/locales/ast.json index 1437d8698..7beb706ad 100644 --- a/src/locales/ast.json +++ b/src/locales/ast.json @@ -78,6 +78,7 @@ "pad.share.emebdcode": "Incrustar URL", "pad.chat": "Chat", "pad.chat.title": "Abrir el chat d'esti bloc.", + "pad.chat.loadmessages": "Cargar m\u00e1s mensaxes", "timeslider.pageTitle": "Eslizador de tiempu de {{appTitle}}", "timeslider.toolbar.returnbutton": "Tornar al bloc", "timeslider.toolbar.authors": "Autores:", @@ -99,6 +100,8 @@ "timeslider.month.october": "d'ochobre", "timeslider.month.november": "de payares", "timeslider.month.december": "d'avientu", + "timeslider.unnamedauthor": "{{num}} autor an\u00f3nimu", + "timeslider.unnamedauthors": "{{num}} autores an\u00f3nimos", "pad.savedrevs.marked": "Esta revisi\u00f3n marcose como revisi\u00f3n guardada", "pad.userlist.entername": "Escribi'l to nome", "pad.userlist.unnamed": "ensin nome", diff --git a/src/locales/az.json b/src/locales/az.json index 1e6439fdc..95c657981 100644 --- a/src/locales/az.json +++ b/src/locales/az.json @@ -50,17 +50,21 @@ "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (A\u00e7\u0131q S\u0259n\u0259d Format\u0131)", "pad.importExport.exportdokuwiki": "DokuWiki", + "pad.importExport.abiword.innerHTML": "Siz yaln\u0131z adi m\u0259tnd\u0259n v\u0259 ya HTML-d\u0259n idxal ed\u0259 bil\u0259rsiniz. \u0130dxal\u0131n daha m\u00fcr\u0259kk\u0259b funksiyalar\u0131 \u00fc\u00e7\u00fcn, z\u0259hm\u0259t olmasa, AbiWord-i qura\u015fd\u0131r\u0131n<\/a>.", "pad.modals.connected": "Ba\u011fland\u0131.", "pad.modals.reconnecting": "Sizin pad yenid\u0259n qo\u015fulur..", "pad.modals.forcereconnect": "M\u0259cbur t\u0259krar\u0259n ba\u011flan", "pad.modals.userdup": "Ba\u015fqa p\u0259nc\u0259r\u0259d\u0259 art\u0131q a\u00e7\u0131qd\u0131r", + "pad.modals.userdup.explanation": "S\u0259n\u0259d, ola bilsin ki, bu kompyuterd\u0259, brauzerin bir ne\u00e7\u0259 p\u0259nc\u0259r\u0259sind\u0259 a\u00e7\u0131lm\u0131\u015fd\u0131r.", "pad.modals.userdup.advice": "Bu p\u0259nc\u0259r\u0259d\u0259n istifad\u0259yl\u0259 yenid\u0259n qo\u015fulun.", "pad.modals.unauth": "\u0130caz\u0259li deyil", "pad.modals.unauth.explanation": "Bu s\u0259hif\u0259y\u0259 baxd\u0131\u011f\u0131n\u0131z vaxt sizin icaz\u0259niz d\u0259yi\u015filib. B\u0259rpa etm\u0259k \u00fc\u015f\u00fcn yenid\u0259n c\u0259hd edin.", "pad.modals.looping": "\u018flaq\u0259 k\u0259sildi.", "pad.modals.looping.explanation": "Sinxronla\u015fd\u0131rma serveri il\u0259 kommunikasiya x\u0259tas\u0131 var.", + "pad.modals.looping.cause": "Ola bilsin ki, siz uy\u011fun olmayan fayrvol v\u0259 ya proksi vasit\u0259si il\u0259 qo\u015fulma\u011fa c\u0259hd g\u00f6st\u0259rirsiniz.", "pad.modals.initsocketfail": "Server \u0259l\u00e7atmazd\u0131r.", "pad.modals.initsocketfail.explanation": "Sinxronla\u015fd\u0131rma serverin\u0259 qo\u015fulma m\u00fcmk\u00fcns\u00fczd\u00fcr.", + "pad.modals.initsocketfail.cause": "Ehtimal ki, bu problem sizin brauzerinizl\u0259 v\u0259 ya internet-birl\u0259\u015fm\u0259nizl\u0259 \u0259laq\u0259d\u0259rdir.", "pad.modals.slowcommit": "\u018flaq\u0259 k\u0259sildi.", "pad.modals.slowcommit.explanation": "Server cavab vermir.", "pad.modals.slowcommit.cause": "Bu \u015f\u0259b\u0259k\u0259 ba\u011flant\u0131s\u0131nda probleml\u0259r yarana bil\u0259r.", @@ -75,6 +79,7 @@ "pad.share.emebdcode": "URL-ni yay\u0131mla", "pad.chat": "S\u00f6hb\u0259t", "pad.chat.title": "Bu pad \u00fc\u00e7\u00fcn chat a\u00e7\u0131n.", + "pad.chat.loadmessages": "Daha \u00e7ox mesaj y\u00fckl\u0259", "timeslider.pageTitle": "{{appTitle}} Vaxt c\u0259dv\u0259li", "timeslider.toolbar.returnbutton": "Pad-a qay\u0131t", "timeslider.toolbar.authors": "M\u00fc\u0259llifl\u0259r:", @@ -96,6 +101,9 @@ "timeslider.month.october": "Oktyabr", "timeslider.month.november": "Noyabr", "timeslider.month.december": "Dekabr", + "timeslider.unnamedauthor": "{{num}} ads\u0131z m\u00fc\u0259llif", + "timeslider.unnamedauthors": "{{num}} ads\u0131z m\u00fc\u0259llifl\u0259r", + "pad.savedrevs.marked": "Bu versiya indi yadda\u015fa saxlanm\u0131\u015f kimi ni\u015fanland\u0131", "pad.userlist.entername": "Ad\u0131n\u0131z\u0131 daxil et", "pad.userlist.unnamed": "ads\u0131z", "pad.userlist.guest": "Qonaq", @@ -104,8 +112,10 @@ "pad.editbar.clearcolors": "B\u00fct\u00fcn s\u0259n\u0259dl\u0259rd\u0259 m\u00fc\u0259lliflik r\u0259ngl\u0259rini t\u0259mizl\u0259?", "pad.impexp.importbutton": "\u0130ndi idxal edin", "pad.impexp.importing": "\u0130dxal...", + "pad.impexp.confirmimport": "Fayl\u0131n idxal\u0131 cari m\u0259tni yenil\u0259y\u0259c\u0259k. Siz \u0259minsinizmi ki, davam etm\u0259k ist\u0259yirsiniz?", "pad.impexp.convertFailed": "Biz bu fayl idxal etm\u0259k m\u00fcmk\u00fcn deyil idi. Xahi\u015f olunur m\u00fcxt\u0259lif s\u0259n\u0259dd\u0259n istifad\u0259 edin v\u0259 ya kopyalay\u0131b yap\u0131\u015fd\u0131rmaq yolundan istifad\u0259 edin", "pad.impexp.uploadFailed": "Y\u00fckl\u0259m\u0259d\u0259 s\u0259hv, xahi\u015f olunur yen\u0259 c\u0259hd edin", "pad.impexp.importfailed": "\u0130dxal zaman\u0131 s\u0259hv", - "pad.impexp.copypaste": "Xahi\u015f edirik kopyalay\u0131b yap\u0131\u015fd\u0131r\u0131n" + "pad.impexp.copypaste": "Xahi\u015f edirik kopyalay\u0131b yap\u0131\u015fd\u0131r\u0131n", + "pad.impexp.exportdisabled": "{{ type}} format\u0131nda ixrac s\u00f6nd\u00fcr\u00fclm\u00fc\u015fd\u00fcr. \u018ftrafl\u0131 informasiya \u00fc\u00e7\u00fcn sistem administratoruna m\u00fcraci\u0259t ediniz." } \ No newline at end of file diff --git a/src/locales/da.json b/src/locales/da.json index 3e785b585..dc6c0f259 100644 --- a/src/locales/da.json +++ b/src/locales/da.json @@ -78,6 +78,7 @@ "pad.share.emebdcode": "Integrerings URL", "pad.chat": "Chat", "pad.chat.title": "\u00c5ben chat for denne pad.", + "pad.chat.loadmessages": "Indl\u00e6s flere meddelelser", "timeslider.pageTitle": "{{appTitle}} Timeslider", "timeslider.toolbar.returnbutton": "Tilbage til pad", "timeslider.toolbar.authors": "Forfattere:", diff --git a/src/locales/de.json b/src/locales/de.json index 1bdbdaf3d..7c51fa912 100644 --- a/src/locales/de.json +++ b/src/locales/de.json @@ -103,6 +103,8 @@ "timeslider.month.october": "Oktober", "timeslider.month.november": "November", "timeslider.month.december": "Dezember", + "timeslider.unnamedauthor": "{{num}} unbenannter Autor", + "timeslider.unnamedauthors": "{{num}} unbenannte Autoren", "pad.savedrevs.marked": "Diese Version wurde jetzt als gespeicherte Version gekennzeichnet", "pad.userlist.entername": "Geben Sie Ihren Namen ein", "pad.userlist.unnamed": "unbenannt", diff --git a/src/locales/es.json b/src/locales/es.json index 57bc35a8d..f0358a902 100644 --- a/src/locales/es.json +++ b/src/locales/es.json @@ -82,6 +82,7 @@ "pad.share.emebdcode": "Incrustar URL", "pad.chat": "Chat", "pad.chat.title": "Abrir el chat para este pad.", + "pad.chat.loadmessages": "Cargar m\u00e1s mensajes", "timeslider.pageTitle": "{{appTitle}} L\u00ednea de tiempo", "timeslider.toolbar.returnbutton": "Volver al pad", "timeslider.toolbar.authors": "Autores:", diff --git a/src/locales/fi.json b/src/locales/fi.json index 9a644bda4..74f7e36ca 100644 --- a/src/locales/fi.json +++ b/src/locales/fi.json @@ -5,7 +5,8 @@ "1": "Jl", "2": "Nedergard", "3": "Nike", - "5": "VezonThunder" + "5": "Veikk0.ma", + "6": "VezonThunder" } }, "index.newPad": "Uusi muistio", @@ -82,6 +83,7 @@ "pad.share.emebdcode": "Upotusosoite", "pad.chat": "Keskustelu", "pad.chat.title": "Avaa keskustelu nykyisest\u00e4 muistiosta.", + "pad.chat.loadmessages": "Lataa lis\u00e4\u00e4 viestej\u00e4", "timeslider.pageTitle": "{{appTitle}} -aikajana", "timeslider.toolbar.returnbutton": "Palaa muistioon", "timeslider.toolbar.authors": "Tekij\u00e4t:", diff --git a/src/locales/it.json b/src/locales/it.json index 8f2a9c995..c60678db9 100644 --- a/src/locales/it.json +++ b/src/locales/it.json @@ -81,6 +81,7 @@ "pad.share.emebdcode": "Incorpora URL", "pad.chat": "Chat", "pad.chat.title": "Apri la chat per questo Pad.", + "pad.chat.loadmessages": "Carica altri messaggi", "timeslider.pageTitle": "Cronologia {{appTitle}}", "timeslider.toolbar.returnbutton": "Ritorna al Pad", "timeslider.toolbar.authors": "Autori:", diff --git a/src/locales/ml.json b/src/locales/ml.json index 79c66bc60..4741fe775 100644 --- a/src/locales/ml.json +++ b/src/locales/ml.json @@ -35,7 +35,7 @@ "pad.settings.myView": "\u0d0e\u0d28\u0d4d\u0d31\u0d46 \u0d15\u0d3e\u0d34\u0d4d\u0d1a", "pad.settings.stickychat": "\u0d24\u0d24\u0d4d\u0d38\u0d2e\u0d2f\u0d02 \u0d38\u0d02\u0d35\u0d3e\u0d26\u0d02 \u0d0e\u0d2a\u0d4d\u0d2a\u0d4b\u0d34\u0d41\u0d02 \u0d38\u0d4d\u0d15\u0d4d\u0d30\u0d40\u0d28\u0d3f\u0d7d \u0d15\u0d3e\u0d23\u0d3f\u0d15\u0d4d\u0d15\u0d41\u0d15", "pad.settings.colorcheck": "\u0d0e\u0d34\u0d41\u0d24\u0d4d\u0d24\u0d41\u0d15\u0d3e\u0d7c\u0d15\u0d4d\u0d15\u0d41\u0d33\u0d4d\u0d33 \u0d28\u0d3f\u0d31\u0d19\u0d4d\u0d19\u0d7e", - "pad.settings.linenocheck": "\u0d0e\u0d23\u0d4d\u0d23\u0d2e\u0d3f\u0d1f\u0d4d\u0d1f \u0d35\u0d30\u0d3f\u0d15\u0d7e", + "pad.settings.linenocheck": "\u0d35\u0d30\u0d3f\u0d15\u0d33\u0d41\u0d1f\u0d46 \u0d15\u0d4d\u0d30\u0d2e\u0d38\u0d02\u0d16\u0d4d\u0d2f", "pad.settings.fontType": "\u0d2b\u0d4b\u0d23\u0d4d\u0d1f\u0d4d \u0d24\u0d30\u0d02:", "pad.settings.fontType.normal": "\u0d38\u0d3e\u0d27\u0d3e\u0d30\u0d23\u0d02", "pad.settings.fontType.monospaced": "\u0d2e\u0d4b\u0d23\u0d4b\u0d38\u0d4d\u0d2a\u0d47\u0d38\u0d4d", @@ -100,19 +100,19 @@ "timeslider.month.october": "\u0d12\u0d15\u0d4d\u0d1f\u0d4b\u0d2c\u0d7c", "timeslider.month.november": "\u0d28\u0d35\u0d02\u0d2c\u0d7c", "timeslider.month.december": "\u0d21\u0d3f\u0d38\u0d02\u0d2c\u0d7c", - "pad.savedrevs.marked": "\u0d08 \u0d2a\u0d24\u0d3f\u0d2a\u0d4d\u0d2a\u0d4d \u0d38\u0d02\u0d30\u0d15\u0d4d\u0d37\u0d3f\u0d1a\u0d4d\u0d1a\u0d24\u0d3e\u0d2f\u0d3f \u0d05\u0d1f\u0d2f\u0d3e\u0d33\u0d2a\u0d4d\u0d2a\u0d46\u0d1f\u0d41\u0d24\u0d4d\u0d24\u0d3f\u0d2f\u0d3f\u0d30\u0d3f\u0d15\u0d4d\u0d15\u0d41\u0d28\u0d4d\u0d28\u0d41", - "pad.userlist.entername": "\u0d28\u0d3f\u0d19\u0d4d\u0d19\u0d33\u0d41\u0d1f\u0d46 \u0d2a\u0d47\u0d30\u0d4d", + "pad.savedrevs.marked": "\u0d08 \u0d28\u0d3e\u0d7e\u0d2a\u0d4d\u0d2a\u0d24\u0d3f\u0d2a\u0d4d\u0d2a\u0d4d \u0d38\u0d47\u0d35\u0d4d \u0d1a\u0d46\u0d2f\u0d4d\u0d24\u0d3f\u0d1f\u0d4d\u0d1f\u0d41\u0d33\u0d4d\u0d33 \u0d28\u0d3e\u0d7e\u0d2a\u0d4d\u0d2a\u0d24\u0d3f\u0d2a\u0d4d\u0d2a\u0d3e\u0d2f\u0d3f \u0d05\u0d1f\u0d2f\u0d3e\u0d33\u0d2a\u0d4d\u0d2a\u0d46\u0d1f\u0d41\u0d24\u0d4d\u0d24\u0d3f\u0d2f\u0d3f\u0d30\u0d3f\u0d15\u0d4d\u0d15\u0d41\u0d28\u0d4d\u0d28\u0d41", + "pad.userlist.entername": "\u0d24\u0d3e\u0d19\u0d4d\u0d15\u0d33\u0d41\u0d1f\u0d46 \u0d2a\u0d47\u0d30\u0d4d \u0d28\u0d7d\u0d15\u0d41\u0d15", "pad.userlist.unnamed": "\u0d2a\u0d47\u0d30\u0d3f\u0d32\u0d4d\u0d32\u0d3e\u0d24\u0d4d\u0d24", "pad.userlist.guest": "\u0d05\u0d24\u0d3f\u0d25\u0d3f", - "pad.userlist.deny": "\u0d24\u0d33\u0d4d\u0d33\u0d3f\u0d15\u0d4d\u0d15\u0d33\u0d2f\u0d41\u0d15", + "pad.userlist.deny": "\u0d28\u0d3f\u0d30\u0d38\u0d3f\u0d15\u0d4d\u0d15\u0d41\u0d15", "pad.userlist.approve": "\u0d05\u0d02\u0d17\u0d40\u0d15\u0d30\u0d3f\u0d15\u0d4d\u0d15\u0d41\u0d15", - "pad.editbar.clearcolors": "\u0d30\u0d1a\u0d2f\u0d3f\u0d24\u0d3e\u0d35\u0d3f\u0d28\u0d46 \u0d38\u0d42\u0d1a\u0d3f\u0d2a\u0d4d\u0d2a\u0d3f\u0d15\u0d4d\u0d15\u0d41\u0d28\u0d4d\u0d28 \u0d28\u0d3f\u0d31\u0d19\u0d4d\u0d19\u0d33\u0d46\u0d32\u0d4d\u0d32\u0d3e\u0d02 \u0d15\u0d33\u0d2f\u0d23\u0d4b?", + "pad.editbar.clearcolors": "\u0d21\u0d4b\u0d15\u0d4d\u0d2f\u0d41\u0d2e\u0d46\u0d28\u0d4d\u0d31\u0d3f\u0d7d \u0d30\u0d1a\u0d2f\u0d3f\u0d24\u0d3e\u0d15\u0d4d\u0d15\u0d33\u0d46 \u0d38\u0d42\u0d1a\u0d3f\u0d2a\u0d4d\u0d2a\u0d3f\u0d15\u0d4d\u0d15\u0d3e\u0d28\u0d3e\u0d2f\u0d3f \u0d28\u0d7d\u0d15\u0d3f\u0d2f\u0d3f\u0d1f\u0d4d\u0d1f\u0d41\u0d33\u0d4d\u0d33 \u0d28\u0d3f\u0d31\u0d19\u0d4d\u0d19\u0d7e \u0d12\u0d34\u0d3f\u0d35\u0d3e\u0d15\u0d4d\u0d15\u0d1f\u0d4d\u0d1f\u0d46?", "pad.impexp.importbutton": "\u0d07\u0d31\u0d15\u0d4d\u0d15\u0d41\u0d2e\u0d24\u0d3f \u0d1a\u0d46\u0d2f\u0d4d\u0d2f\u0d41\u0d15", "pad.impexp.importing": "\u0d07\u0d31\u0d15\u0d4d\u0d15\u0d41\u0d2e\u0d24\u0d3f \u0d1a\u0d46\u0d2f\u0d4d\u0d2f\u0d41\u0d28\u0d4d\u0d28\u0d41...", "pad.impexp.confirmimport": "\u0d12\u0d30\u0d41 \u0d2a\u0d4d\u0d30\u0d2e\u0d3e\u0d23\u0d02 \u0d07\u0d31\u0d15\u0d4d\u0d15\u0d41\u0d2e\u0d24\u0d3f \u0d1a\u0d46\u0d2f\u0d4d\u0d2f\u0d41\u0d28\u0d4d\u0d28\u0d24\u0d4d \u0d28\u0d3f\u0d32\u0d35\u0d3f\u0d32\u0d41\u0d33\u0d4d\u0d33 \u0d0e\u0d34\u0d41\u0d24\u0d4d\u0d24\u0d41\u0d15\u0d7e \u0d28\u0d37\u0d4d\u0d1f\u0d2a\u0d4d\u0d2a\u0d46\u0d1f\u0d3e\u0d28\u0d3f\u0d1f\u0d2f\u0d3e\u0d15\u0d4d\u0d15\u0d41\u0d02, \u0d24\u0d41\u0d1f\u0d30\u0d23\u0d2e\u0d46\u0d28\u0d4d\u0d28\u0d4d \u0d09\u0d31\u0d2a\u0d4d\u0d2a\u0d3e\u0d23\u0d4b?", - "pad.impexp.convertFailed": "\u0d08 \u0d2a\u0d4d\u0d30\u0d2e\u0d3e\u0d23\u0d02 \u0d07\u0d31\u0d15\u0d4d\u0d15\u0d41\u0d2e\u0d24\u0d3f \u0d1a\u0d46\u0d2f\u0d4d\u0d2f\u0d3e\u0d7b \u0d38\u0d3e\u0d27\u0d3f\u0d1a\u0d4d\u0d1a\u0d3f\u0d32\u0d4d\u0d32. \u0d26\u0d2f\u0d35\u0d3e\u0d2f\u0d3f \u0d2e\u0d31\u0d4d\u0d31\u0d4a\u0d30\u0d41 \u0d21\u0d4b\u0d15\u0d4d\u0d2f\u0d41\u0d2e\u0d46\u0d28\u0d4d\u0d31\u0d4d \u0d2b\u0d4b\u0d7c\u0d2e\u0d3e\u0d31\u0d4d\u0d31\u0d4d \u0d09\u0d2a\u0d2f\u0d4b\u0d17\u0d3f\u0d15\u0d4d\u0d15\u0d41\u0d15\u0d2f\u0d4b, \u0d38\u0d4d\u0d35\u0d28\u0d4d\u0d24\u0d2e\u0d3e\u0d2f\u0d3f \u0d15\u0d4b\u0d2a\u0d4d\u0d2a\u0d3f \u0d2a\u0d47\u0d38\u0d4d\u0d31\u0d4d\u0d31\u0d4d \u0d1a\u0d46\u0d2f\u0d4d\u0d2f\u0d41\u0d15\u0d2f\u0d4b \u0d1a\u0d46\u0d2f\u0d4d\u0d2f\u0d42", + "pad.impexp.convertFailed": "\u0d08 \u0d2a\u0d4d\u0d30\u0d2e\u0d3e\u0d23\u0d02 \u0d07\u0d31\u0d15\u0d4d\u0d15\u0d41\u0d2e\u0d24\u0d3f \u0d1a\u0d46\u0d2f\u0d4d\u0d2f\u0d3e\u0d7b \u0d38\u0d3e\u0d27\u0d3f\u0d1a\u0d4d\u0d1a\u0d3f\u0d32\u0d4d\u0d32. \u0d26\u0d2f\u0d35\u0d3e\u0d2f\u0d3f \u0d2e\u0d31\u0d4d\u0d31\u0d4a\u0d30\u0d41 \u0d21\u0d4b\u0d15\u0d4d\u0d2f\u0d41\u0d2e\u0d46\u0d28\u0d4d\u0d31\u0d4d \u0d2b\u0d4b\u0d7c\u0d2e\u0d3e\u0d31\u0d4d\u0d31\u0d4d \u0d09\u0d2a\u0d2f\u0d4b\u0d17\u0d3f\u0d15\u0d4d\u0d15\u0d41\u0d15\u0d2f\u0d4b, \u0d38\u0d4d\u0d35\u0d28\u0d4d\u0d24\u0d2e\u0d3e\u0d2f\u0d3f \u0d2a\u0d15\u0d7c\u0d24\u0d4d\u0d24\u0d3f \u0d1a\u0d47\u0d7c\u0d15\u0d4d\u0d15\u0d41\u0d15\u0d2f\u0d4b \u0d1a\u0d46\u0d2f\u0d4d\u0d2f\u0d41\u0d15", "pad.impexp.uploadFailed": "\u0d05\u0d2a\u0d4d\u200c\u200c\u0d32\u0d4b\u0d21\u0d4d \u0d2a\u0d30\u0d3e\u0d1c\u0d2f\u0d2a\u0d4d\u0d2a\u0d46\u0d1f\u0d4d\u0d1f\u0d41. \u0d26\u0d2f\u0d35\u0d3e\u0d2f\u0d3f \u0d35\u0d40\u0d23\u0d4d\u0d1f\u0d41\u0d02 \u0d36\u0d4d\u0d30\u0d2e\u0d3f\u0d15\u0d4d\u0d15\u0d41\u0d15", "pad.impexp.importfailed": "\u0d07\u0d31\u0d15\u0d4d\u0d15\u0d41\u0d2e\u0d24\u0d3f \u0d2a\u0d30\u0d3e\u0d1c\u0d2f\u0d2a\u0d4d\u0d2a\u0d46\u0d1f\u0d4d\u0d1f\u0d41", - "pad.impexp.copypaste": "\u0d26\u0d2f\u0d35\u0d3e\u0d2f\u0d3f \u0d15\u0d4b\u0d2a\u0d4d\u0d2a\u0d3f \u0d2a\u0d47\u0d38\u0d4d\u0d31\u0d4d\u0d31\u0d4d \u0d1a\u0d46\u0d2f\u0d4d\u0d2f\u0d41\u0d15", - "pad.impexp.exportdisabled": "{{type}} \u0d2b\u0d4b\u0d7c\u0d2e\u0d3e\u0d31\u0d4d\u0d31\u0d3f\u0d7d \u0d15\u0d2f\u0d31\u0d4d\u0d31\u0d41\u0d2e\u0d24\u0d3f \u0d1a\u0d46\u0d2f\u0d4d\u0d2f\u0d41\u0d28\u0d4d\u0d28\u0d24\u0d4d \u0d24\u0d1f\u0d1e\u0d4d\u0d1e\u0d3f\u0d30\u0d3f\u0d15\u0d4d\u0d15\u0d41\u0d28\u0d4d\u0d28\u0d41. \u0d26\u0d2f\u0d35\u0d3e\u0d2f\u0d3f \u0d24\u0d3e\u0d19\u0d4d\u0d15\u0d33\u0d41\u0d1f\u0d46 \u0d38\u0d3f\u0d38\u0d4d\u0d31\u0d4d\u0d31\u0d02 \u0d05\u0d21\u0d4d\u0d2e\u0d3f\u0d28\u0d3f\u0d38\u0d4d\u0d31\u0d4d\u0d31\u0d4d\u0d30\u0d47\u0d31\u0d4d\u0d31\u0d31\u0d41\u0d2e\u0d3e\u0d2f\u0d3f \u0d2c\u0d28\u0d4d\u0d27\u0d2a\u0d4d\u0d2a\u0d46\u0d1f\u0d41\u0d15" + "pad.impexp.copypaste": "\u0d26\u0d2f\u0d35\u0d3e\u0d2f\u0d3f \u0d2a\u0d15\u0d7c\u0d24\u0d4d\u0d24\u0d3f \u0d1a\u0d47\u0d7c\u0d15\u0d4d\u0d15\u0d41\u0d15", + "pad.impexp.exportdisabled": "{{type}} \u0d2b\u0d4b\u0d7c\u0d2e\u0d3e\u0d31\u0d4d\u0d31\u0d3f\u0d7d \u0d15\u0d2f\u0d31\u0d4d\u0d31\u0d41\u0d2e\u0d24\u0d3f \u0d1a\u0d46\u0d2f\u0d4d\u0d2f\u0d41\u0d28\u0d4d\u0d28\u0d24\u0d4d \u0d24\u0d1f\u0d1e\u0d4d\u0d1e\u0d3f\u0d30\u0d3f\u0d15\u0d4d\u0d15\u0d41\u0d28\u0d4d\u0d28\u0d41. \u0d15\u0d42\u0d1f\u0d41\u0d24\u0d7d \u0d35\u0d3f\u0d35\u0d30\u0d19\u0d4d\u0d19\u0d7e\u0d15\u0d4d\u0d15\u0d4d \u0d24\u0d3e\u0d19\u0d4d\u0d15\u0d33\u0d41\u0d1f\u0d46 \u0d38\u0d3f\u0d38\u0d4d\u0d31\u0d4d\u0d31\u0d02 \u0d05\u0d21\u0d4d\u0d2e\u0d3f\u0d28\u0d3f\u0d38\u0d4d\u0d1f\u0d4d\u0d30\u0d47\u0d31\u0d4d\u0d31\u0d31\u0d41\u0d2e\u0d3e\u0d2f\u0d3f \u0d2c\u0d28\u0d4d\u0d27\u0d2a\u0d4d\u0d2a\u0d46\u0d1f\u0d41\u0d15." } \ No newline at end of file diff --git a/src/locales/nl.json b/src/locales/nl.json index 018060533..9b1c773bf 100644 --- a/src/locales/nl.json +++ b/src/locales/nl.json @@ -100,6 +100,8 @@ "timeslider.month.october": "oktober", "timeslider.month.november": "november", "timeslider.month.december": "december", + "timeslider.unnamedauthor": "{{num}} onbekende auteur", + "timeslider.unnamedauthors": "{{num}} onbekende auteurs", "pad.savedrevs.marked": "Deze versie is nu gemarkeerd als opgeslagen versie", "pad.userlist.entername": "Geef uw naam op", "pad.userlist.unnamed": "zonder naam", diff --git a/src/locales/oc.json b/src/locales/oc.json index 79e94db7f..08390825b 100644 --- a/src/locales/oc.json +++ b/src/locales/oc.json @@ -1,4 +1,9 @@ { + "@metadata": { + "authors": [ + "Cedric31" + ] + }, "index.newPad": "Pad nov\u00e8l", "index.createOpenPad": "o crear\/dobrir un Pad intitulat :", "pad.toolbar.bold.title": "Gras (Ctrl-B)", @@ -44,15 +49,29 @@ "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", "pad.importExport.exportdokuwiki": "DokuWiki", + "pad.importExport.abiword.innerHTML": "Pod\u00e8tz pas importar que de formats t\u00e8xte brut o html. Per de foncionalitats d'importacion mai evoluadas, installatz abiword<\/a>.", "pad.modals.connected": "Connectat.", "pad.modals.reconnecting": "Reconnexion cap a v\u00f2stre Pad...", "pad.modals.forcereconnect": "For\u00e7ar la reconnexion.", "pad.modals.userdup": "Dob\u00e8rt dins una autra fen\u00e8stra", + "pad.modals.userdup.explanation": "Sembla qu'aqueste Pad es dob\u00e8rt dins mai d'una fen\u00e8stra de v\u00f2stre navigador sus aqueste ordenador.", + "pad.modals.userdup.advice": "Se reconnectar en utilizant aquesta fen\u00e8stra.", "pad.modals.unauth": "Pas autorizat", + "pad.modals.unauth.explanation": "V\u00f2stras permissions son estadas cambiadas al moment de l'afichatge d'aquesta pagina. Ensajatz de vos reconnectar.", "pad.modals.looping": "Desconnectat", + "pad.modals.looping.explanation": "Av\u00e8m un probl\u00e8ma de comunicacion amb lo servidor de sincronizacion.", + "pad.modals.looping.cause": "Es possible que v\u00f2stra connexion si\u00e1 protegida per un parafu\u00f2c incompatible o un servidor proxy incompatible.", + "pad.modals.initsocketfail": "Lo servidor es introbable.", + "pad.modals.initsocketfail.explanation": "Impossible de se connectar al servidor de sincronizacion.", + "pad.modals.initsocketfail.cause": "Lo probl\u00e8ma p\u00f2t venir de v\u00f2stre navigador web o de v\u00f2stra connexion Internet.", "pad.modals.slowcommit": "Desconnectat", "pad.modals.slowcommit.explanation": "Lo servidor respond pas.", + "pad.modals.slowcommit.cause": "Aqueste probl\u00e8ma p\u00f2t venir d'una marrida connectivitat a la ret.", "pad.modals.deleted": "Suprimit.", + "pad.modals.deleted.explanation": "Aqueste Pad es estat suprimit.", + "pad.modals.disconnected": "S\u00e8tz estat desconnectat.", + "pad.modals.disconnected.explanation": "La connexion al servidor a fracassat.", + "pad.modals.disconnected.cause": "Es possible que lo servidor si\u00e1 indisponible. Informatz-nos-ne se lo probl\u00e8ma persist\u00eds.", "pad.share": "Partejar aqueste Pad", "pad.share.readonly": "Lectura sola", "pad.share.link": "Ligam", @@ -60,9 +79,12 @@ "pad.chat": "Chat", "pad.chat.title": "Dobrir lo chat associat a aqueste pad.", "pad.chat.loadmessages": "Cargar mai de messatges.", + "timeslider.pageTitle": "Istoric dinamic de {{appTitle}}", + "timeslider.toolbar.returnbutton": "Retorn a aqueste Pad.", "timeslider.toolbar.authors": "Autors :", "timeslider.toolbar.authorsList": "Pas cap d'autor", "timeslider.toolbar.exportlink.title": "Exportar", + "timeslider.exportCurrent": "Exportar la version actuala en\u00a0:", "timeslider.version": "Version {{version}}", "timeslider.saved": "Enregistrat lo {{day}} {{month}} {{year}}", "timeslider.dateformat": "{{month}}\/{{day}}\/{{year}} {{hours}}:{{minutes}}:{{seconds}}", @@ -78,18 +100,19 @@ "timeslider.month.october": "Octobre", "timeslider.month.november": "Novembre", "timeslider.month.december": "Decembre", + "pad.savedrevs.marked": "Aquesta revision es ara marcada coma revision enregistrada", "pad.userlist.entername": "Entratz v\u00f2stre nom", "pad.userlist.unnamed": "sens nom", "pad.userlist.guest": "Convidat", "pad.userlist.deny": "Refusar", "pad.userlist.approve": "Aprovar", + "pad.editbar.clearcolors": "Escafar las colors de paternitat dins tot lo document ?", "pad.impexp.importbutton": "Importar ara", "pad.impexp.importing": "Imp\u00f2rt en cors...", + "pad.impexp.confirmimport": "Importar un fichi\u00e8r espotir\u00e0 lo t\u00e8xte actual del bl\u00f2t. S\u00e8tz segur que lo vol\u00e8tz far ?", + "pad.impexp.convertFailed": "Pod\u00e8m pas importar aqueste fichi\u00e8r. Utilizatz un autre format de document o fas\u00e8tz un copiar\/pegar manual", + "pad.impexp.uploadFailed": "Lo telecargament a fracassat, reensajatz", "pad.impexp.importfailed": "Frac\u00e0s de l'importacion", "pad.impexp.copypaste": "Copiatz\/pegatz", - "@metadata": { - "authors": [ - "Cedric31" - ] - } + "pad.impexp.exportdisabled": "Exportar al format {{type}} es desactivat. Contactatz v\u00f2stre administrator del sist\u00e8ma per mai de detalhs." } \ No newline at end of file diff --git a/src/locales/pl.json b/src/locales/pl.json index e52f7c32a..3481cafc5 100644 --- a/src/locales/pl.json +++ b/src/locales/pl.json @@ -80,6 +80,7 @@ "pad.share.emebdcode": "Kod do umieszczenia", "pad.chat": "Czat", "pad.chat.title": "Otw\u00f3rz czat dla tego dokumentu.", + "pad.chat.loadmessages": "Za\u0142aduj wi\u0119cej wiadomo\u015bci", "timeslider.pageTitle": "O\u015b czasu {{appTitle}}", "timeslider.toolbar.returnbutton": "Powr\u00f3\u0107 do dokumentu", "timeslider.toolbar.authors": "Autorzy:", diff --git a/src/locales/ps.json b/src/locales/ps.json index 574fb1ad0..6589c77f7 100644 --- a/src/locales/ps.json +++ b/src/locales/ps.json @@ -1 +1,51 @@ -{"pad.toolbar.bold.title":"زغرد (Ctrl-B)","pad.toolbar.italic.title":"رېوند (Ctrl-I)","pad.toolbar.undo.title":"ناکړل (Ctrl-Z)","pad.toolbar.redo.title":"بياکړل (Ctrl-Y)","pad.toolbar.settings.title":"امستنې","pad.colorpicker.save":"خوندي کول","pad.colorpicker.cancel":"ناګارل","pad.loading":"برسېرېدنې کې دی...","pad.settings.myView":"زما کتنه","pad.settings.fontType":"ليکبڼې ډول:","pad.settings.fontType.normal":"نورمال","pad.settings.fontType.monospaced":"مونوسپېس","pad.settings.language":"ژبه:","pad.importExport.exporthtml":"اچ ټي ام اېل","pad.importExport.exportplain":"ساده متن","pad.importExport.exportword":"مايکروسافټ ورډ","pad.importExport.exportpdf":"پي ډي اېف","pad.importExport.exportopen":"ODF (اوپن ډاکومنټ فارمټ)","pad.modals.deleted":"ړنګ شو.","pad.share.readonly":"يوازې لوستنه","pad.share.link":"تړنه","pad.share.emebdcode":"يو آر اېل ټومبل","pad.chat":"بانډار","timeslider.toolbar.authors":"ليکوال:","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":"ډيسمبر"} \ No newline at end of file +{ + "@metadata": { + "authors": [ + "Ahmed-Najib-Biabani-Ibrahimkhel" + ] + }, + "pad.toolbar.bold.title": "\u0632\u063a\u0631\u062f (Ctrl-B)", + "pad.toolbar.italic.title": "\u0631\u06d0\u0648\u0646\u062f (Ctrl-I)", + "pad.toolbar.undo.title": "\u0646\u0627\u06a9\u0693\u0644 (Ctrl-Z)", + "pad.toolbar.redo.title": "\u0628\u064a\u0627\u06a9\u0693\u0644 (Ctrl-Y)", + "pad.toolbar.settings.title": "\u0627\u0645\u0633\u062a\u0646\u06d0", + "pad.colorpicker.save": "\u062e\u0648\u0646\u062f\u064a \u06a9\u0648\u0644", + "pad.colorpicker.cancel": "\u0646\u0627\u06ab\u0627\u0631\u0644", + "pad.loading": "\u0628\u0631\u0633\u06d0\u0631\u06d0\u062f\u0646\u06d0 \u06a9\u06d0 \u062f\u06cc...", + "pad.wrongPassword": "\u067e\u067c\u0646\u0648\u0645 \u0645\u0648 \u0633\u0645 \u0646\u0647 \u0648", + "pad.settings.myView": "\u0632\u0645\u0627 \u06a9\u062a\u0646\u0647", + "pad.settings.fontType": "\u0644\u064a\u06a9\u0628\u06bc\u06d0 \u0689\u0648\u0644:", + "pad.settings.fontType.normal": "\u0646\u0648\u0631\u0645\u0627\u0644", + "pad.settings.fontType.monospaced": "\u0645\u0648\u0646\u0648\u0633\u067e\u06d0\u0633", + "pad.settings.language": "\u0698\u0628\u0647:", + "pad.importExport.exporthtml": "\u0627\u0686 \u067c\u064a \u0627\u0645 \u0627\u06d0\u0644", + "pad.importExport.exportplain": "\u0633\u0627\u062f\u0647 \u0645\u062a\u0646", + "pad.importExport.exportword": "\u0645\u0627\u064a\u06a9\u0631\u0648\u0633\u0627\u0641\u067c \u0648\u0631\u0689", + "pad.importExport.exportpdf": "\u067e\u064a \u0689\u064a \u0627\u06d0\u0641", + "pad.importExport.exportopen": "ODF (\u0627\u0648\u067e\u0646 \u0689\u0627\u06a9\u0648\u0645\u0646\u067c \u0641\u0627\u0631\u0645\u067c)", + "pad.modals.deleted": "\u0693\u0646\u06ab \u0634\u0648.", + "pad.share.readonly": "\u064a\u0648\u0627\u0632\u06d0 \u0644\u0648\u0633\u062a\u0646\u0647", + "pad.share.link": "\u062a\u0693\u0646\u0647", + "pad.share.emebdcode": "\u064a\u0648 \u0622\u0631 \u0627\u06d0\u0644 \u067c\u0648\u0645\u0628\u0644", + "pad.chat": "\u0628\u0627\u0646\u0689\u0627\u0631", + "pad.chat.loadmessages": "\u0646\u0648\u0631 \u067e\u064a\u063a\u0627\u0645\u0648\u0646\u0647 \u0628\u0631\u0633\u06d0\u0631\u0648\u0644", + "timeslider.toolbar.authors": "\u0644\u064a\u06a9\u0648\u0627\u0644:", + "timeslider.toolbar.authorsList": "\u0628\u06d0 \u0644\u064a\u06a9\u0648\u0627\u0644\u0647", + "timeslider.month.january": "\u062c\u0646\u0648\u0631\u064a", + "timeslider.month.february": "\u0641\u0628\u0631\u0648\u0631\u064a", + "timeslider.month.march": "\u0645\u0627\u0631\u0686", + "timeslider.month.april": "\u0627\u067e\u0631\u06d0\u0644", + "timeslider.month.may": "\u0645\u06cd", + "timeslider.month.june": "\u062c\u0648\u0646", + "timeslider.month.july": "\u062c\u0648\u0644\u0627\u06cc", + "timeslider.month.august": "\u0627\u06ab\u0633\u067c", + "timeslider.month.september": "\u0633\u06d0\u067e\u062a\u0645\u0628\u0631", + "timeslider.month.october": "\u0627\u06a9\u062a\u0648\u0628\u0631", + "timeslider.month.november": "\u0646\u0648\u0645\u0628\u0631", + "timeslider.month.december": "\u0689\u064a\u0633\u0645\u0628\u0631", + "pad.userlist.entername": "\u0646\u0648\u0645 \u0645\u0648 \u0648\u0631\u06a9\u0693\u06cd", + "pad.userlist.unnamed": "\u0628\u06d0 \u0646\u0648\u0645\u0647", + "pad.userlist.guest": "\u0645\u06d0\u0644\u0645\u0647", + "pad.userlist.deny": "\u0631\u062f\u0648\u0644", + "pad.userlist.approve": "\u0645\u0646\u0644" +} \ No newline at end of file diff --git a/src/locales/pt-br.json b/src/locales/pt-br.json index 78c9ed3f5..6562681a6 100644 --- a/src/locales/pt-br.json +++ b/src/locales/pt-br.json @@ -78,6 +78,7 @@ "pad.share.emebdcode": "Incorporar o URL", "pad.chat": "Bate-papo", "pad.chat.title": "Abrir o bate-papo desta nota.", + "pad.chat.loadmessages": "Carregar mais mensagens", "timeslider.pageTitle": "Linha do tempo de {{appTitle}}", "timeslider.toolbar.returnbutton": "Retornar para a nota", "timeslider.toolbar.authors": "Autores:", diff --git a/src/locales/ru.json b/src/locales/ru.json index 7eb34c97e..1f2fbda63 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -2,6 +2,7 @@ "@metadata": { "authors": [ "Amire80", + "DCamer", "Eleferen", "Volkov" ] @@ -28,6 +29,8 @@ "pad.colorpicker.save": "\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c", "pad.colorpicker.cancel": "\u041e\u0442\u043c\u0435\u043d\u0430", "pad.loading": "\u0417\u0430\u0433\u0440\u0443\u0437\u043a\u0430...", + "pad.passwordRequired": "\u0412\u0430\u043c \u043d\u0443\u0436\u0435\u043d \u043f\u0430\u0440\u043e\u043b\u044c \u0434\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430", + "pad.permissionDenied": "\u0423 \u0432\u0430\u0441 \u043d\u0435\u0442 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u044f \u043d\u0430 \u0434\u043e\u0441\u0442\u0443\u043f", "pad.wrongPassword": "\u041d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0439 \u043f\u0430\u0440\u043e\u043b\u044c", "pad.settings.padSettings": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430", "pad.settings.myView": "\u041c\u043e\u0439 \u0432\u0438\u0434", @@ -49,9 +52,11 @@ "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442 OpenOffice)", "pad.importExport.exportdokuwiki": "DokuWiki", + "pad.importExport.abiword.innerHTML": "\u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0438\u0437 \u043e\u0431\u044b\u0447\u043d\u043e\u0433\u043e \u0442\u0435\u043a\u0441\u0442\u0430 \u0438\u043b\u0438 HTML. \u0414\u043b\u044f \u0431\u043e\u043b\u0435\u0435 \u043f\u0440\u043e\u0434\u0432\u0438\u043d\u0443\u0442\u044b\u0445 \u0444\u0443\u043d\u043a\u0446\u0438\u0439 \u0438\u043c\u043f\u043e\u0440\u0442\u0430, \u043f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0435 AbiWord<\/a>.", "pad.modals.connected": "\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d.", "pad.modals.reconnecting": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a \u0432\u0430\u0448\u0435\u043c\u0443 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0443", "pad.modals.forcereconnect": "\u041f\u0440\u0438\u043d\u0443\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u043f\u0435\u0440\u0435\u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435", + "pad.modals.userdup": "\u041e\u0442\u043a\u0440\u044b\u0442\u043e \u0432 \u0434\u0440\u0443\u0433\u043e\u043c \u043e\u043a\u043d\u0435", "pad.modals.userdup.explanation": "\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442, \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u043e\u0442\u043a\u0440\u044b\u0442 \u0431\u043e\u043b\u0435\u0435 \u0447\u0435\u043c \u0432 \u043e\u0434\u043d\u043e\u043c \u043e\u043a\u043d\u0435 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430 \u043d\u0430 \u044d\u0442\u043e\u043c \u043a\u043e\u043c\u043f\u044c\u044e\u0442\u0435\u0440\u0435.", "pad.modals.userdup.advice": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u044d\u0442\u043e\u0433\u043e \u043e\u043a\u043d\u0430.", "pad.modals.unauth": "\u041d\u0435 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d", @@ -76,22 +81,41 @@ "pad.share.emebdcode": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044c URL", "pad.chat": "\u0427\u0430\u0442", "pad.chat.title": "\u041e\u0442\u043a\u0440\u044b\u0442\u044c \u0447\u0430\u0442 \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430.", + "pad.chat.loadmessages": "\u0415\u0449\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f", "timeslider.pageTitle": "\u0412\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f \u0448\u043a\u0430\u043b\u0430 {{appTitle}}", "timeslider.toolbar.returnbutton": "\u041a \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0443", "timeslider.toolbar.authors": "\u0410\u0432\u0442\u043e\u0440\u044b:", "timeslider.toolbar.authorsList": "\u041d\u0435\u0442 \u0430\u0432\u0442\u043e\u0440\u043e\u0432", + "timeslider.toolbar.exportlink.title": "\u042d\u043a\u0441\u043f\u043e\u0440\u0442", "timeslider.exportCurrent": "\u042d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0442\u0435\u043a\u0443\u0449\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e \u043a\u0430\u043a:", "timeslider.version": "\u0412\u0435\u0440\u0441\u0438\u044f {{version}}", "timeslider.saved": "\u0421\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u043e {{day}}.{{month}}.{{year}}", + "timeslider.dateformat": "{{month}}\/{{day}}\/{{year}} {{hours}}:{{minutes}}:{{seconds}}", "timeslider.month.january": "\u044f\u043d\u0432\u0430\u0440\u044c", "timeslider.month.february": "\u0444\u0435\u0432\u0440\u0430\u043b\u044c", "timeslider.month.march": "\u043c\u0430\u0440\u0442", "timeslider.month.april": "\u0430\u043f\u0440\u0435\u043b\u044c", "timeslider.month.may": "\u043c\u0430\u0439", + "timeslider.month.june": "\u0438\u044e\u043d\u044c", + "timeslider.month.july": "\u0438\u044e\u043b\u044c", + "timeslider.month.august": "\u0430\u0432\u0433\u0443\u0441\u0442", + "timeslider.month.september": "\u0441\u0435\u043d\u0442\u044f\u0431\u0440\u044c", + "timeslider.month.october": "\u043e\u043a\u0442\u044f\u0431\u0440\u044c", + "timeslider.month.november": "\u043d\u043e\u044f\u0431\u0440\u044c", + "timeslider.month.december": "\u0434\u0435\u043a\u0430\u0431\u0440\u044c", + "pad.savedrevs.marked": "\u042d\u0442\u0430 \u0432\u0435\u0440\u0441\u0438\u044f \u0442\u0435\u043f\u0435\u0440\u044c \u043f\u043e\u043c\u0435\u0447\u0435\u043d\u0430 \u043a\u0430\u043a \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u043d\u0430\u044f", "pad.userlist.entername": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0432\u0430\u0448\u0435 \u0438\u043c\u044f", + "pad.userlist.unnamed": "\u0431\u0435\u0437\u044b\u043c\u044f\u043d\u043d\u044b\u0439", "pad.userlist.guest": "\u0413\u043e\u0441\u0442\u044c", + "pad.userlist.deny": "\u041e\u0442\u043a\u043b\u043e\u043d\u0438\u0442\u044c", + "pad.userlist.approve": "\u0423\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c", + "pad.editbar.clearcolors": "\u041e\u0447\u0438\u0441\u0442\u0438\u0442\u044c \u0430\u0432\u0442\u043e\u0440\u0441\u043a\u0438\u0435 \u0446\u0432\u0435\u0442\u0430 \u0432\u043e \u0432\u0441\u0435\u043c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0435?", "pad.impexp.importbutton": "\u0418\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0435\u0439\u0447\u0430\u0441", "pad.impexp.importing": "\u0418\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u2026", + "pad.impexp.confirmimport": "\u0418\u043c\u043f\u043e\u0440\u0442 \u0444\u0430\u0439\u043b\u0430 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0438\u0448\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u0442\u0435\u043a\u0441\u0442. \u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u044c?", + "pad.impexp.convertFailed": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u043e\u0442 \u0444\u0430\u0439\u043b. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u0434\u0440\u0443\u0433\u043e\u0439 \u0444\u043e\u0440\u043c\u0430\u0442 \u0438\u043b\u0438 \u0441\u043a\u043e\u043f\u0438\u0440\u0443\u0439\u0442\u0435 \u0432\u0440\u0443\u0447\u043d\u0443\u044e", "pad.impexp.uploadFailed": "\u0417\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u043d\u0435 \u0443\u0434\u0430\u043b\u0430\u0441\u044c, \u043f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0435\u0449\u0451 \u0440\u0430\u0437", - "pad.impexp.importfailed": "\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438" + "pad.impexp.importfailed": "\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438", + "pad.impexp.copypaste": "\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0441\u043a\u043e\u043f\u0438\u0440\u0443\u0439\u0442\u0435", + "pad.impexp.exportdisabled": "\u042d\u043a\u0441\u043f\u043e\u0440\u0442 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 {{type}} \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d. \u0414\u043b\u044f \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u0435\u0441\u044c \u043a \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u043e\u043c\u0443 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u0443." } \ No newline at end of file diff --git a/src/locales/sl.json b/src/locales/sl.json index bf834206c..c7cffa6f0 100644 --- a/src/locales/sl.json +++ b/src/locales/sl.json @@ -78,6 +78,7 @@ "pad.share.emebdcode": "Vstavi naslov URL", "pad.chat": "Klepet", "pad.chat.title": "Odpri klepetalno okno dokumenta.", + "pad.chat.loadmessages": "Nalo\u017ei ve\u010d sporo\u010dil", "timeslider.pageTitle": "Zgodovina dokumenta {{appTitle}}", "timeslider.toolbar.returnbutton": "Vrni se na dokument", "timeslider.toolbar.authors": "Autorji:", diff --git a/src/locales/uk.json b/src/locales/uk.json index af035c651..ae6cbbb61 100644 --- a/src/locales/uk.json +++ b/src/locales/uk.json @@ -80,6 +80,7 @@ "pad.share.emebdcode": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438 URL", "pad.chat": "\u0427\u0430\u0442", "pad.chat.title": "\u0412\u0456\u0434\u043a\u0440\u0438\u0442\u0438 \u0447\u0430\u0442 \u0434\u043b\u044f \u0446\u044c\u043e\u0433\u043e \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0443.", + "pad.chat.loadmessages": "\u0417\u0430\u0432\u0430\u043d\u0442\u0430\u0436\u0438\u0442\u0438 \u0431\u0456\u043b\u044c\u0448\u0435 \u043f\u043e\u0432\u0456\u0434\u043e\u043c\u043b\u0435\u043d\u044c", "timeslider.pageTitle": "\u0427\u0430\u0441\u043e\u0432\u0430 \u0448\u043a\u0430\u043b\u0430 {{appTitle}}", "timeslider.toolbar.returnbutton": "\u041f\u043e\u0432\u0435\u0440\u043d\u0443\u0442\u0438\u0441\u044c \u0434\u043e \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0443", "timeslider.toolbar.authors": "\u0410\u0432\u0442\u043e\u0440\u0438:", diff --git a/src/locales/zh-hant.json b/src/locales/zh-hant.json index c91e2fe37..6ee1e3777 100644 --- a/src/locales/zh-hant.json +++ b/src/locales/zh-hant.json @@ -78,6 +78,7 @@ "pad.share.emebdcode": "\u5d4c\u5165\u7db2\u5740", "pad.chat": "\u804a\u5929", "pad.chat.title": "\u6253\u958b\u6b64pad\u7684\u804a\u5929\u3002", + "pad.chat.loadmessages": "\u8f09\u5165\u66f4\u591a\u8a0a\u606f", "timeslider.pageTitle": "{{appTitle}}\u6642\u9593\u8ef8", "timeslider.toolbar.returnbutton": "\u8fd4\u56de\u5230pad", "timeslider.toolbar.authors": "\u4f5c\u8005\uff1a", From f974136d0cf4e5555a81ebd655095efaa10b4643 Mon Sep 17 00:00:00 2001 From: mluto Date: Mon, 28 Jan 2013 10:13:37 +0100 Subject: [PATCH 16/22] Jump to revision given in URL, add current revision to URL --- src/static/js/broadcast_slider.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/static/js/broadcast_slider.js b/src/static/js/broadcast_slider.js index 221666de0..821c92215 100644 --- a/src/static/js/broadcast_slider.js +++ b/src/static/js/broadcast_slider.js @@ -106,6 +106,7 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded) function setSliderPosition(newpos) { newpos = Number(newpos); + window.location.hash = "#" + newpos; if (newpos < 0 || newpos > sliderLength) return; $("#ui-slider-handle").css('left', newpos * ($("#ui-slider-bar").width() - 2) / (sliderLength * 1.0)); $("a.tlink").map(function() @@ -481,6 +482,18 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded) } $("#timeslider").show(); + + var startPos = clientVars.collab_client_vars.rev; + if(window.location.hash.length > 1) + { + var hashRev = Number(window.location.hash.substr(1)); + if(!isNaN(hashRev)) + { + // this is necessary because of the socket.io-event which loads the changesets + setTimeout(function() { setSliderPosition(hashRev); }, 1); + } + } + setSliderLength(clientVars.collab_client_vars.rev); setSliderPosition(clientVars.collab_client_vars.rev); From 437856188239e0f5d6b71c0de2083e9819e996b0 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 28 Jan 2013 16:52:23 +0000 Subject: [PATCH 17/22] change to createDiffHTML --- src/node/db/API.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/node/db/API.js b/src/node/db/API.js index a700a4915..f99a43afd 100644 --- a/src/node/db/API.js +++ b/src/node/db/API.js @@ -658,14 +658,14 @@ exports.getChatHead = function(padID, callback) } /** -createDiff(padID, startRev, endRev) returns an object of diffs from 2 points in a pad +createDiffHTML(padID, startRev, endRev) returns an object of diffs from 2 points in a pad Example returns: {"code":0,"message":"ok","data":{"html":"Welcome to Etherpad Lite!

This pad text is synchronized as you type, so that everyone viewing this page sees the same text. This allows you to collaborate seamlessly on documents!

Get involved with Etherpad at
http://etherpad.org
aw

","authors":["a.HKIv23mEbachFYfH",""]}} {"code":4,"message":"no or wrong API Key","data":null} */ -exports.createDiff = function(padID, startRev, endRev, callback){ +exports.createDiffHTML = function(padID, startRev, endRev, callback){ //check if rev is a number if(startRev !== undefined && typeof startRev != "number") { From 51eff0d659a8364b4a1fb6712a072873b8dbbd59 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 28 Jan 2013 16:53:29 +0000 Subject: [PATCH 18/22] change to createDiffHTML --- 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 1f91c737e..9f86277a0 100644 --- a/src/node/handler/APIHandler.js +++ b/src/node/handler/APIHandler.js @@ -180,7 +180,7 @@ var version = , "deleteGroup" : ["groupID"] , "listPads" : ["groupID"] , "listAllPads" : [] - , "createDiff" : ["padID", "startRev", "endRev"] + , "createDiffHTML" : ["padID", "startRev", "endRev"] , "createPad" : ["padID", "text"] , "createGroupPad" : ["groupID", "padName", "text"] , "createAuthor" : ["name"] From a239158b49159f660546d0f358c34e9ba1bfc199 Mon Sep 17 00:00:00 2001 From: mluto Date: Mon, 28 Jan 2013 20:17:34 +0100 Subject: [PATCH 19/22] Renamed paramSettings to getParameters, to cause less confusion --- src/static/js/pad.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/static/js/pad.js b/src/static/js/pad.js index 9f0fba7fa..1b244aea1 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.js @@ -107,7 +107,7 @@ function randomString() // * the parameter was supplied and matches checkVal // * the parameter was supplied and checkVal is null // callback: the function to call when all above succeeds, `val` is the value supplied by the user -var paramSettings = [ +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: "showChat", checkVal: "false", callback: function(val) { $('#chaticon').hide(); } }, @@ -126,9 +126,9 @@ function getParams() { var params = getUrlVars() - for(var i = 0; i < paramSettings.length; i++) + for(var i = 0; i < getParameters.length; i++) { - var setting = paramSettings[i]; + var setting = getParameters[i]; var value = params[setting.name]; if(value && (value == setting.checkVal || setting.checkVal == null)) From 3002807741175a7b4f508346bcac9fcb7cf46fc2 Mon Sep 17 00:00:00 2001 From: mluto Date: Mon, 28 Jan 2013 21:12:50 +0100 Subject: [PATCH 20/22] Added tests for revision-jumping --- tests/frontend/specs/timeslider_revisions.js | 64 ++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/tests/frontend/specs/timeslider_revisions.js b/tests/frontend/specs/timeslider_revisions.js index af59051ab..de8ca52f8 100644 --- a/tests/frontend/specs/timeslider_revisions.js +++ b/tests/frontend/specs/timeslider_revisions.js @@ -57,4 +57,68 @@ describe("timeslider", function(){ }, 6000); }, revs*timePerRev); }); + + it("changes the url when clicking on the timeslider", function(done) { + var inner$ = helper.padInner$; + var chrome$ = helper.padChrome$; + + // make some changes to produce 7 revisions + var timePerRev = 900 + , revs = 7; + this.timeout(revs*timePerRev+10000); + for(var i=0; i < revs; i++) { + setTimeout(function() { + // enter 'a' in the first text element + inner$("div").first().sendkeys('a'); + }, timePerRev*i); + } + + setTimeout(function() { + // go to timeslider + $('#iframe-container iframe').attr('src', $('#iframe-container iframe').attr('src')+'/timeslider'); + + setTimeout(function() { + var timeslider$ = $('#iframe-container iframe')[0].contentWindow.$; + 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 + var e = new jQuery.Event('mousedown'); + e.clientX = e.pageX = 150; + e.clientY = e.pageY = 60; + $sliderBar.trigger(e); + + helper.waitFor(function(){ + return $('#iframe-container iframe')[0].contentWindow.location.hash != oldUrl; + }, 6000).always(function(){ + expect( $('#iframe-container iframe')[0].contentWindow.location.hash ).not.to.eql( oldUrl ); + done(); + }); + }, 6000); + }, revs*timePerRev); + }); + + it("jumps to a revision given in the url", function(done) { + var inner$ = helper.padInner$; + var chrome$ = helper.padChrome$; + this.timeout(11000); + inner$("div").first().sendkeys('a'); + + setTimeout(function() { + // go to timeslider + $('#iframe-container iframe').attr('src', $('#iframe-container iframe').attr('src')+'/timeslider#0'); + var timeslider$; + + helper.waitFor(function(){ + timeslider$ = $('#iframe-container iframe')[0].contentWindow.$; + return timeslider$ && timeslider$('#padcontent').text().length == 230; + }, 6000).always(function(){ + expect( timeslider$('#padcontent').text().length ).to.eql( 230 ); + done(); + }); + }, 2500); + }); }); From 0b90e5752be46113b3fcc9ae71a71c68c8a335f3 Mon Sep 17 00:00:00 2001 From: mluto Date: Mon, 28 Jan 2013 21:38:56 +0100 Subject: [PATCH 21/22] Added a test to check the export-url --- tests/frontend/specs/timeslider_revisions.js | 27 ++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/frontend/specs/timeslider_revisions.js b/tests/frontend/specs/timeslider_revisions.js index de8ca52f8..52f487643 100644 --- a/tests/frontend/specs/timeslider_revisions.js +++ b/tests/frontend/specs/timeslider_revisions.js @@ -121,4 +121,31 @@ describe("timeslider", function(){ }); }, 2500); }); + + it("checks the export url", function(done) { + var inner$ = helper.padInner$; + var chrome$ = helper.padChrome$; + this.timeout(11000); + inner$("div").first().sendkeys('a'); + + setTimeout(function() { + // go to timeslider + $('#iframe-container iframe').attr('src', $('#iframe-container iframe').attr('src')+'/timeslider#0'); + var timeslider$; + var exportLink; + + helper.waitFor(function(){ + timeslider$ = $('#iframe-container iframe')[0].contentWindow.$; + if(!timeslider$) + return false; + exportLink = timeslider$('#exportplaina').attr('href'); + if(!exportLink) + return false; + return exportLink.substr(exportLink.length - 12) == "0/export/txt"; + }, 6000).always(function(){ + expect( exportLink.substr(exportLink.length - 12) ).to.eql( "0/export/txt" ); + done(); + }); + }, 2500); + }); }); From 01fe5c183d0e123c5682ac5274df94103b6bd291 Mon Sep 17 00:00:00 2001 From: mluto Date: Mon, 28 Jan 2013 21:44:21 +0100 Subject: [PATCH 22/22] Only set url if the revsion will actually be loaded --- src/static/js/broadcast_slider.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/static/js/broadcast_slider.js b/src/static/js/broadcast_slider.js index 821c92215..08ac08b5a 100644 --- a/src/static/js/broadcast_slider.js +++ b/src/static/js/broadcast_slider.js @@ -106,8 +106,8 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded) function setSliderPosition(newpos) { newpos = Number(newpos); - window.location.hash = "#" + newpos; if (newpos < 0 || newpos > sliderLength) return; + window.location.hash = "#" + newpos; $("#ui-slider-handle").css('left', newpos * ($("#ui-slider-bar").width() - 2) / (sliderLength * 1.0)); $("a.tlink").map(function() {