diff options
58 files changed, 1086 insertions, 261 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 84d85000..0e453735 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,24 @@ +# 1.5.7 + * NEW: Add support for intermediate CA certificates for ssl + * NEW: Provide a script to clean up before running etherpad + * NEW: Use ctrl+shift+1 to do a ordered list + * NEW: Show versions of plugins on startup + * NEW: Add author on padCreate and padUpdate hook + * Fix: switchToPad method + * Fix: Dead keys + * Fix: Preserve new lines in copy-pasted text + * Fix: Compatibility mode on IE + * Fix: Content Collector to get the class of the DOM-node + * Fix: Timeslider export links + * Fix: Double prompt on file upload + * Fix: setText() replaces the entire pad text + * Fix: Accessibility features on embedded pads + * Fix: Tidy HTML before abiword conversion + * Fix: Remove edit buttons in read-only view + * Fix: Disable user input in read-only view + * Fix: Pads end with a single newline, rather than two newlines + * Fix: Toolbar and chat for mobile devices + # 1.5.6 * Fix: Error on windows installations @@ -4,7 +4,7 @@ # About Etherpad is a really-real time collaborative editor maintained by the Etherpad Community. -Etherpad is written in JavaScript(99.9%) on both the server and client so it's easy for developers to maintain and add new features. Because of this Etherpad has tons of customizations that you can leverage. +Etherpad is written in JavaScript (99.9%) on both the server and client so it's easy for developers to maintain and add new features. Because of this Etherpad has tons of customizations that you can leverage. Etherpad is designed to be easily embeddable and provides a [HTTP API](https://github.com/ether/etherpad-lite/wiki/HTTP-API) that allows your web application to manage pads, users and groups. It is recommended to use the [available client implementations](https://github.com/ether/etherpad-lite/wiki/HTTP-API-client-libraries) in order to interact with this API. diff --git a/bin/installDeps.sh b/bin/installDeps.sh index f2a3aafc..fbbbbb24 100755 --- a/bin/installDeps.sh +++ b/bin/installDeps.sh @@ -46,13 +46,14 @@ fi #check node version NODE_VERSION=$(node --version) NODE_V_MINOR=$(echo $NODE_VERSION | cut -d "." -f 1-2) +NODE_V_MAIN=$(echo $NODE_VERSION | cut -d "." -f 1) #iojs version checking added if hash iojs 2>/dev/null; then IOJS_VERSION=$(iojs --version) fi -if [ ! $NODE_V_MINOR = "v0.10" ] && [ ! $NODE_V_MINOR = "v0.11" ] && [ ! $NODE_V_MINOR = "v0.12" ]; then +if [ ! $NODE_V_MINOR = "v0.10" ] && [ ! $NODE_V_MINOR = "v0.11" ] && [ ! $NODE_V_MINOR = "v0.12" ] && [ ! $NODE_V_MAIN = "v4" ]; then if [ ! $IOJS_VERSION ]; then - echo "You're running a wrong version of node, or io.js is not installed. You're using $NODE_VERSION, we need v0.10.x, v0.11.x or v0.12.x" >&2 + echo "You're running a wrong version of node, or io.js is not installed. You're using $NODE_VERSION, we need node v0.10.x or higher" >&2 exit 1 fi fi diff --git a/bin/rebuildPad.js b/bin/rebuildPad.js index c8383342..60c5f4ed 100644 --- a/bin/rebuildPad.js +++ b/bin/rebuildPad.js @@ -79,6 +79,9 @@ async.series([ newPad.pool.numToAttrib = oldPad.pool.numToAttrib; for(var curRevNum = 0; curRevNum <= newRevHead; curRevNum++) { db.db.get("pad:" + padId + ":revs:" + curRevNum, function(err, rev) { + if (rev.meta) { + throw "The specified revision number could not be found."; + } var newRevNum = ++newPad.head; var newRevId = "pad:" + newPad.id + ":revs:" + newRevNum; db.db.set(newRevId, rev); diff --git a/doc/api/hooks_server-side.md b/doc/api/hooks_server-side.md index 79879b2f..962c6e12 100644 --- a/doc/api/hooks_server-side.md +++ b/doc/api/hooks_server-side.md @@ -357,7 +357,7 @@ Things in context: 1. Pad object -This hook will allow a plug-in developer to include more properties and attributes to support during HTML Export. An Array should be returned. +This hook will allow a plug-in developer to include more properties and attributes to support during HTML Export. An Array should be returned. If a value in this array is a string, the exported HTML will contain tags like `<tag_name>` for the content where attributes are `['tag_name', 'true']`; if a value in this array is a pair `['tag_name', 'value']`, the exported HTML will contain tags like `<span data-tag_name="value">` for the content where attributes are `['tag_name', 'value']`. Example: ``` @@ -368,6 +368,15 @@ exports.exportHtmlAdditionalTags = function(hook, pad, cb){ }; ``` +Example when attributes are stores as `['color', 'red']` on the attribute pool: +``` +// Add the props to be supported in export +exports.exportHtmlAdditionalTags = function(hook, pad, cb){ + var padId = pad.id; + cb([["color", "red"], ["color", "blue"]]); +}; +``` + ## userLeave Called from src/node/handler/PadMessageHandler.js @@ -384,3 +393,20 @@ exports.userLeave = function(hook, session, callback) { console.log('%s left pad %s', session.author, session.padId); }; ``` + +### clientReady +Called from src/node/handler/PadMessageHandler.js + +This in context: + +1. message + +This hook gets called when handling a CLIENT_READY which is the first message from the client to the server. + +Example: + +``` +exports.clientReady = function(hook, message) { + console.log('Client has entered the pad' + message.padId); +}; +``` diff --git a/doc/api/http_api.md b/doc/api/http_api.md index 59510a75..9809814b 100644 --- a/doc/api/http_api.md +++ b/doc/api/http_api.md @@ -380,6 +380,16 @@ returns the chatHead (last number of the last chat-message) of the pad * `{code: 0, message:"ok", data: {chatHead: 42}}` * `{code: 1, message:"padID does not exist", data: null}` +#### appendChatMessage(padID, text, authorID [, time]) + * API >= 1.2.12 + +creates a chat message, saves it to the database and sends it to all connected clients of this pad + + +*Example returns:* + +* `{code: 0, message:"ok", data: null}` +* `{code: 1, message:"text is no string", data: null}` ### Pad Group pads are normal pads, but with the name schema GROUPID$PADNAME. A security manager controls access of them and its forbidden for normal pads to include a $ in the name. diff --git a/settings.json.template b/settings.json.template index 6a344cb1..f29bcbc0 100644 --- a/settings.json.template +++ b/settings.json.template @@ -66,7 +66,7 @@ "lang": "en-gb" }, - /* Shoud we suppress errors from being visible in the default Pad Text? */ + /* Should we suppress errors from being visible in the default Pad Text? */ "suppressErrorsInPadText" : false, /* Users must have a session to access pads. This effectively allows only group pads to be accessed. */ @@ -86,10 +86,14 @@ may cause problems during deployment. Set to 0 to disable caching */ "maxAge" : 21600, // 60 * 60 * 6 = 6 hours - /* This is the path to the Abiword executable. Setting it to null, disables abiword. + /* This is the absolute path to the Abiword executable. Setting it to null, disables abiword. Abiword is needed to advanced import/export features of pads*/ "abiword" : null, + /* This is the absolute path to the soffice executable. Setting it to null, disables LibreOffice exporting. + LibreOffice can be used in lieu of Abiword to export pads */ + "soffice" : null, + /* This is the path to the Tidy executable. Setting it to null, disables Tidy. Tidy is used to improve the quality of exported pads*/ "tidyHtml" : null, @@ -131,6 +135,11 @@ // Allow Load Testing tools to hit the Etherpad Instance. Warning this will disable security on the instance. "loadTest": false, + // Disable indentation on new line when previous line ends with some special chars (':', '[', '(', '{') + /* + "indentationOnNewLine": false, + */ + /* The toolbar buttons configuration. "toolbar": { "left": [ diff --git a/src/locales/ar.json b/src/locales/ar.json index 153faae8..33c6beb3 100644 --- a/src/locales/ar.json +++ b/src/locales/ar.json @@ -6,7 +6,8 @@ "Alami", "Meno25", "Test Create account", - "محمد أحمد عبد الفتاح" + "محمد أحمد عبد الفتاح", + "Haytham morsy" ] }, "index.newPad": "باد جديد", @@ -97,6 +98,9 @@ "timeslider.exportCurrent": "تصدير النسخة الحالية ك:", "timeslider.version": "إصدار {{version}}", "timeslider.saved": "محفوظ {{month}} {{day}}, {{year}}", + "timeslider.playPause": "تشغيل / إيقاف مؤقت محتويات الباد", + "timeslider.backRevision": "عد إلى مراجعة في هذه الباد", + "timeslider.forwardRevision": "انطلق إلى مراجعة في هذه الباد", "timeslider.dateformat": "{{day}}/{{month}}/{{year}} {{hours}}:{{minutes}}:{{seconds}}", "timeslider.month.january": "يناير", "timeslider.month.february": "فبراير", diff --git a/src/locales/br.json b/src/locales/br.json index 93879844..6a60fa3a 100644 --- a/src/locales/br.json +++ b/src/locales/br.json @@ -22,7 +22,7 @@ "pad.toolbar.clearAuthorship.title": "Diverkañ al livioù oc'h anaout an aozerien (Ktrl+Pennlizherenn+C)", "pad.toolbar.import_export.title": "Enporzhiañ/Ezporzhiañ eus/war-zu ur furmad restr disheñvel", "pad.toolbar.timeslider.title": "Istor dinamek", - "pad.toolbar.savedRevision.title": "Doareoù enrollet", + "pad.toolbar.savedRevision.title": "Enrollañ an adweladenn", "pad.toolbar.settings.title": "Arventennoù", "pad.toolbar.embed.title": "Rannañ hag enframmañ ar pad-mañ", "pad.toolbar.showusers.title": "Diskwelet implijerien ar Pad", @@ -96,6 +96,8 @@ "timeslider.version": "Stumm {{version}}", "timeslider.saved": "Enrollañ {{day}} {{month}} {{year}}", "timeslider.playPause": "Lenn / Ehan endalc'hoù ar pad", + "timeslider.backRevision": "Kilit eus un adweladenn er pad-mañ", + "timeslider.forwardRevision": "Araogiñ un adweladenn er pad-mañ", "timeslider.dateformat": "{{month}}/{{day}}/{{year}} {{hours}}:{{minutes}}:{{seconds}}", "timeslider.month.january": "Genver", "timeslider.month.february": "C'hwevrer", diff --git a/src/locales/ca.json b/src/locales/ca.json index 638b645f..f6526f1b 100644 --- a/src/locales/ca.json +++ b/src/locales/ca.json @@ -6,7 +6,8 @@ "Pitort", "Toniher", "Macofe", - "Joan manel" + "Joan manel", + "Eduardo Martinez" ] }, "index.newPad": "Nou pad", @@ -97,6 +98,9 @@ "timeslider.exportCurrent": "Exporta la versió actual com a:", "timeslider.version": "Versió {{version}}", "timeslider.saved": "Desat {{month}} {{day}}, {{year}}", + "timeslider.playPause": "Reproducció / Pausa els continguts del pad", + "timeslider.backRevision": "Tornar una revisió enrera en aquest Pad", + "timeslider.forwardRevision": "Anar una revisió endavant en aquest Pad", "timeslider.dateformat": "{{month}}/{{day}}/{{year}} {{hours}}:{{minutes}}:{{seconds}}", "timeslider.month.january": "Gener", "timeslider.month.february": "Febrer", diff --git a/src/locales/dty.json b/src/locales/dty.json index 242b96c2..fde36512 100644 --- a/src/locales/dty.json +++ b/src/locales/dty.json @@ -50,5 +50,15 @@ "pad.importExport.exportpdf": "पिडिएफ", "pad.importExport.exportopen": "ओडिएफ(खुल्ला कागजात ढाँचा)", "pad.modals.connected": "जोडीयाको", - "pad.modals.reconnecting": "तमरो प्याडमि आजि: जडान हुन्नाछ" + "pad.modals.reconnecting": "तमरो प्याडमि आजि: जडान हुन्नाछ", + "pad.modals.initsocketfail": "सर्भरमा पहुँच पुर्याउन नाइसकियो ।", + "pad.modals.deleted": "मेटियाको", + "pad.modals.deleted.explanation": "यो प्याड हटाइसक्याको छ ।", + "pad.modals.disconnected": "तमरो जडान अवरुद्ध भयो ।", + "pad.modals.disconnected.explanation": "तमरो सर्भरसितको जडान अवरुद्ध भयो", + "pad.share": "यस प्यडलाई बाड्न्या", + "pad.share.readonly": "पड्या मात्तरै", + "pad.share.link": "लिङ्क", + "pad.share.emebdcode": "URL थप्प्या", + "pad.chat": "कुरणिकानी" } diff --git a/src/locales/eo.json b/src/locales/eo.json index fd7b7c1f..c4526728 100644 --- a/src/locales/eo.json +++ b/src/locales/eo.json @@ -94,6 +94,9 @@ "timeslider.exportCurrent": "Elporti la nunan version kiel:", "timeslider.version": "Versio {{version}}", "timeslider.saved": "Konservita la {{day}}an de {{month}}, {{year}}", + "timeslider.playPause": "Ludi / paŭzi la enhavojn de la teksto", + "timeslider.backRevision": "Reiri unu version en ĉi tiu teksto", + "timeslider.forwardRevision": "Antaŭeniri unu version en ĉi tiu teksto", "timeslider.dateformat": "{{day}}-{{month}}-{{year}} {{hours}}:{{minutes}}:{{seconds}}", "timeslider.month.january": "januaro", "timeslider.month.february": "februaro", diff --git a/src/locales/es.json b/src/locales/es.json index 9d4265b8..b2659063 100644 --- a/src/locales/es.json +++ b/src/locales/es.json @@ -75,7 +75,7 @@ "pad.modals.looping.cause": "Puede deberse a que te conectes a través de un proxy o un cortafuegos incompatible.", "pad.modals.initsocketfail": "Servidor incalcanzable.", "pad.modals.initsocketfail.explanation": "No se pudo conectar al servidor de sincronización.", - "pad.modals.initsocketfail.cause": "Probablemente debido a un problema en tu navegador o en tu conexión a internet.", + "pad.modals.initsocketfail.cause": "Probablemente debido a un problema en tu navegador o en tu conexión a Internet.", "pad.modals.slowcommit.explanation": "El servidor no responde.", "pad.modals.slowcommit.cause": "Puede deberse a problemas con tu conexión de red.", "pad.modals.badChangeset.explanation": "Has hecho una edición clasificada como ilegal por el servidor de sincronización.", @@ -132,7 +132,7 @@ "pad.impexp.confirmimport": "Al importar un archivo se borrará el contenido actual del pad. ¿Estás seguro de que quieres continuar?", "pad.impexp.convertFailed": "No pudimos importar este archivo. Inténtalo con un formato diferente o copia y pega manualmente.", "pad.impexp.padHasData": "No hemos podido importar este archivo porque este pad ya ha tenido cambios. Importa a un nuevo pad.", - "pad.impexp.uploadFailed": "El envío falló. Intentalo de nuevo.", + "pad.impexp.uploadFailed": "El envío falló. Inténtalo de nuevo.", "pad.impexp.importfailed": "Fallo al importar", "pad.impexp.copypaste": "Intenta copiar y pegar", "pad.impexp.exportdisabled": "La exportación al formato {{type}} está desactivada. Contacta a tu administrador de sistemas." diff --git a/src/locales/ia.json b/src/locales/ia.json index e7f5cc2b..64d3bf6e 100644 --- a/src/locales/ia.json +++ b/src/locales/ia.json @@ -33,6 +33,7 @@ "pad.settings.padSettings": "Configuration del pad", "pad.settings.myView": "Mi vista", "pad.settings.stickychat": "Chat sempre visibile", + "pad.settings.chatandusers": "Monstrar chat e usatores", "pad.settings.colorcheck": "Colores de autor", "pad.settings.linenocheck": "Numeros de linea", "pad.settings.rtlcheck": "Leger le contento de dextra a sinistra?", @@ -91,6 +92,9 @@ "timeslider.exportCurrent": "Exportar le version actual como:", "timeslider.version": "Version {{version}}", "timeslider.saved": "Salveguardate le {{day}} de {{month}} {{year}}", + "timeslider.playPause": "Reproducer/pausar le contento del pad", + "timeslider.backRevision": "Recular un version in iste pad", + "timeslider.forwardRevision": "Avantiar un version in iste pad", "timeslider.dateformat": "{{year}}-{{month}}-{{day}} {{hours}}:{{minutes}}:{{seconds}}", "timeslider.month.january": "januario", "timeslider.month.february": "februario", @@ -106,6 +110,7 @@ "timeslider.month.december": "decembre", "timeslider.unnamedauthors": "{{num}} {[plural(num) one: autor, other: autores ]} sin nomine", "pad.savedrevs.marked": "Iste version es ora marcate como version salveguardate", + "pad.savedrevs.timeslider": "Tu pote vider versiones salveguardate con le chronologia glissante.", "pad.userlist.entername": "Entra tu nomine", "pad.userlist.unnamed": "sin nomine", "pad.userlist.guest": "Invitato", diff --git a/src/locales/it.json b/src/locales/it.json index 082535fc..c3841e58 100644 --- a/src/locales/it.json +++ b/src/locales/it.json @@ -97,6 +97,9 @@ "timeslider.exportCurrent": "Esporta la versione corrente come:", "timeslider.version": "Versione {{version}}", "timeslider.saved": "Salvato {{day}} {{month}} {{year}}", + "timeslider.playPause": "Riproduzione / Pausa contenuti Pad", + "timeslider.backRevision": "Vai indietro di una versione in questo Pad", + "timeslider.forwardRevision": "Vai avanti di una versione in questo Pad", "timeslider.dateformat": "{{day}}/{{month}}/{{year}} {{hours}}:{{minutes}}:{{seconds}}", "timeslider.month.january": "gennaio", "timeslider.month.february": "febbraio", diff --git a/src/locales/ko.json b/src/locales/ko.json index 4a71d8ee..87f4eba7 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -3,21 +3,23 @@ "authors": [ "Hym411", "아라", - "Revi" + "Revi", + "Kurousagi", + "SeoJeongHo" ] }, "index.newPad": "새 패드", "index.createOpenPad": "또는 다음 이름으로 패드 만들기/열기:", - "pad.toolbar.bold.title": "굵게 (Ctrl-B)", - "pad.toolbar.italic.title": "기울임 (Ctrl-I)", - "pad.toolbar.underline.title": "밑줄 (Ctrl-U)", + "pad.toolbar.bold.title": "굵은꼴 (Ctrl+B)", + "pad.toolbar.italic.title": "기울임꼴 (Ctrl+I)", + "pad.toolbar.underline.title": "밑줄 (Ctrl+U)", "pad.toolbar.strikethrough.title": "취소선 (Ctrl+5)", "pad.toolbar.ol.title": "순서 있는 목록 (Ctrl+Shift+N)", "pad.toolbar.ul.title": "순서 없는 목록 (Ctrl+Shift+L)", "pad.toolbar.indent.title": "들여쓰기 (TAB)", "pad.toolbar.unindent.title": "내어쓰기 (Shift+TAB)", - "pad.toolbar.undo.title": "실행 취소 (Ctrl-Z)", - "pad.toolbar.redo.title": "다시 실행 (Ctrl-Y)", + "pad.toolbar.undo.title": "실행 취소 (Ctrl+Z)", + "pad.toolbar.redo.title": "다시 실행 (Ctrl+Y)", "pad.toolbar.clearAuthorship.title": "저자의 색 지우기 (Ctrl+Shift+C)", "pad.toolbar.import_export.title": "다른 파일 형식으로 가져오기/내보내기", "pad.toolbar.timeslider.title": "시간슬라이더", @@ -48,6 +50,7 @@ "pad.importExport.import": "텍스트 파일이나 문서 올리기", "pad.importExport.importSuccessful": "성공!", "pad.importExport.export": "다음으로 현재 패드 내보내기:", + "pad.importExport.exportetherpad": "Etherpad", "pad.importExport.exporthtml": "HTML", "pad.importExport.exportplain": "일반 텍스트", "pad.importExport.exportword": "Microsoft Word", @@ -93,6 +96,9 @@ "timeslider.exportCurrent": "현재 버전으로 내보내기:", "timeslider.version": "버전 {{version}}", "timeslider.saved": "{{year}}년 {{month}} {{day}}일에 저장함", + "timeslider.playPause": "시작 / 패드 내용을 일시 중지", + "timeslider.backRevision": "패드의 수정판으로 다시 가기", + "timeslider.forwardRevision": "패드의 수정판을 앞으로 이동 시키기", "timeslider.dateformat": "{{year}}년/{{month}}/{{day}}일 {{hours}}:{{minutes}}:{{seconds}}", "timeslider.month.january": "1월", "timeslider.month.february": "2월", @@ -108,6 +114,7 @@ "timeslider.month.december": "12월", "timeslider.unnamedauthors": "이름 없는 {[plural(num) one: 저자, other: 저자 ]} {{num}}명", "pad.savedrevs.marked": "이 판은 이제 저장한 판으로 표시합니다.", + "pad.savedrevs.timeslider": "당신은 타임슬라이더를 통해 저장된 버전을 볼 수 있습니다", "pad.userlist.entername": "이름을 입력하세요", "pad.userlist.unnamed": "이름없음", "pad.userlist.guest": "손님", @@ -118,6 +125,7 @@ "pad.impexp.importing": "가져오는 중...", "pad.impexp.confirmimport": "파일을 가져오면 패드의 현재 텍스트를 덮어쓰게 됩니다. 진행하시겠습니까?", "pad.impexp.convertFailed": "이 파일을 가져올 수 없습니다. 다른 문서 형식을 사용하거나 수동으로 복사하여 붙여넣으세요", + "pad.impexp.padHasData": "우리는 이 파일을 가져올수 없었습니다. 이 패드는 이미 수정되었으니, 새 패드를 가져와 주십시오", "pad.impexp.uploadFailed": "올리기를 실패했습니다. 다시 시도하세요", "pad.impexp.importfailed": "가져오기를 실패했습니다", "pad.impexp.copypaste": "복사하여 붙여넣으세요", diff --git a/src/locales/ksh.json b/src/locales/ksh.json index 955f9755..f6d0c3bb 100644 --- a/src/locales/ksh.json +++ b/src/locales/ksh.json @@ -14,7 +14,7 @@ "pad.toolbar.ul.title": "Leß met Pongkte (Strg+Jruhß+L)", "pad.toolbar.indent.title": "Enjerök (TAB)", "pad.toolbar.unindent.title": "Ußjerök (Jruhßschreff+TAB)", - "pad.toolbar.undo.title": "Retuur nämme (Strg-Z)", + "pad.toolbar.undo.title": "Retuhr nämme (Strg+Z)", "pad.toolbar.redo.title": "Norrens (Strg-Y)", "pad.toolbar.clearAuthorship.title": "Donn dä Schriiver ier Färve fottnämme (Strg+Jruhß+C)", "pad.toolbar.import_export.title": "Ongerscheidlijje Dattei_Fommaate empotteere udder äxpotteere", @@ -24,7 +24,7 @@ "pad.toolbar.embed.title": "Donn dat Pädd öffentlesch maache un enbenge", "pad.toolbar.showusers.title": "Verbonge Metschriiver aanzeije", "pad.colorpicker.save": "Faßhallde", - "pad.colorpicker.cancel": "Ophüüre", + "pad.colorpicker.cancel": "Ophüre", "pad.loading": "Ben aam Lahde …", "pad.noCookie": "Dat Pläzje wood nit jevonge. Don dat en Dingem Brauser zohlohße!", "pad.passwordRequired": "Do bruchs e Paßwoot för heh dat Pädd.", @@ -76,7 +76,7 @@ "pad.modals.deleted.explanation": "Dat Pädd es fottjeschmeße woode.", "pad.modals.disconnected": "Do bes nit mih verbonge.", "pad.modals.disconnected.explanation": "De Verbendong mem ẞööver es fott.", - "pad.modals.disconnected.cause": "Dä ẞööver künnt nit mieh loufe.\nSidd_esu jood und saad ons Bescheid, wann dadd esu bliiv.", + "pad.modals.disconnected.cause": "Dä ẞööver künnt nit mih loufe.\nSidd_esu jood und saad ons Bescheid, wann dadd esu bliiv.", "pad.share": "Maach heh dat Pädd öffentlesch", "pad.share.readonly": "Nor för ze Lässe", "pad.share.link": "Lengk", @@ -85,7 +85,7 @@ "pad.chat.title": "Maach dä Klaaf för heh dat Pädd op", "pad.chat.loadmessages": "Mih Nohreeschte lahde …", "timeslider.pageTitle": "{{appTitle}} - Verjangeheid affschpelle", - "timeslider.toolbar.returnbutton": "Jangk retuur nohm Pädd", + "timeslider.toolbar.returnbutton": "Jangk retuhr nohm Pädd", "timeslider.toolbar.authors": "De Schrihver:", "timeslider.toolbar.authorsList": "Kein Schriivere", "timeslider.toolbar.exportlink.title": "Äxpotteere", diff --git a/src/locales/lv.json b/src/locales/lv.json index ee402d33..b860055a 100644 --- a/src/locales/lv.json +++ b/src/locales/lv.json @@ -3,7 +3,8 @@ "authors": [ "Admresdeserv.", "Jmg.cmdi", - "Papuass" + "Papuass", + "Silraks" ] }, "pad.toolbar.bold.title": "Treknrakstā (CTRL + B)", @@ -60,6 +61,7 @@ "pad.share": "Koplietot šo pad", "pad.share.readonly": "Tikai lasāms", "pad.share.link": "Saite", + "pad.chat": "Čats", "timeslider.toolbar.authors": "Autori:", "timeslider.toolbar.authorsList": "Nav autoru", "timeslider.toolbar.exportlink.title": "Eksportēt", @@ -80,5 +82,6 @@ "pad.userlist.guest": "Viesis", "pad.impexp.importbutton": "Importēt tūlīt", "pad.impexp.importing": "Importē...", + "pad.impexp.uploadFailed": "Augšupielāde neizdevās, lūdzu, mēģiniet vēlreiz", "pad.impexp.importfailed": "Imports neizdevās" } diff --git a/src/locales/ml.json b/src/locales/ml.json index 680df0c2..371e1ceb 100644 --- a/src/locales/ml.json +++ b/src/locales/ml.json @@ -5,7 +5,8 @@ "Clockery", "Hrishikesh.kb", "Praveenp", - "Santhosh.thottingal" + "Santhosh.thottingal", + "Nesi" ] }, "index.newPad": "പുതിയ പാഡ്", @@ -30,12 +31,14 @@ "pad.colorpicker.save": "സേവ് ചെയ്യുക", "pad.colorpicker.cancel": "റദ്ദാക്കുക", "pad.loading": "ശേഖരിക്കുന്നു...", + "pad.noCookie": "കുക്കി കണ്ടെത്താനായില്ല. ദയവായി താങ്കളുടെ ബ്രൗസറിൽ കുക്കികൾ അനുവദിക്കുക!", "pad.passwordRequired": "ഈ പാഡ് ഉപയോഗിക്കുന്നതിനായി ഒരു രഹസ്യവാക്ക് നൽകേണ്ടതാണ്", "pad.permissionDenied": "ഈ പാഡ് കാണുവാൻ താങ്കൾക്ക് അനുമതിയില്ല", "pad.wrongPassword": "താങ്കൾ നല്കിയ രഹസ്യവാക്ക് തെറ്റായിരുന്നു", "pad.settings.padSettings": "പാഡ് സജ്ജീകരണങ്ങൾ", "pad.settings.myView": "എന്റെ കാഴ്ച", "pad.settings.stickychat": "തത്സമയസംവാദം എപ്പോഴും സ്ക്രീനിൽ കാണിക്കുക", + "pad.settings.chatandusers": "ഉപയോക്താക്കളേയും ചാറ്റും കാണിക്കുക", "pad.settings.colorcheck": "എഴുത്തുകാർക്കുള്ള നിറങ്ങൾ", "pad.settings.linenocheck": "വരികളുടെ ക്രമസംഖ്യ", "pad.settings.rtlcheck": "ഉള്ളടക്കം വലത്തുനിന്ന് ഇടത്തോട്ടാണോ വായിക്കേണ്ടത്?", @@ -48,6 +51,7 @@ "pad.importExport.import": "എന്തെങ്കിലും എഴുത്തു പ്രമാണമോ രേഖയോ അപ്ലോഡ് ചെയ്യുക", "pad.importExport.importSuccessful": "വിജയകരം!", "pad.importExport.export": "ഇപ്പോഴത്തെ പാഡ് ഇങ്ങനെ കയറ്റുമതി ചെയ്യുക:", + "pad.importExport.exportetherpad": "ഈതർപാഡ്", "pad.importExport.exporthtml": "എച്ച്.റ്റി.എം.എൽ.", "pad.importExport.exportplain": "വെറും എഴുത്ത്", "pad.importExport.exportword": "മൈക്രോസോഫ്റ്റ് വേഡ്", @@ -93,6 +97,8 @@ "timeslider.exportCurrent": "ഈ പതിപ്പ് ഇങ്ങനെ എടുക്കുക:", "timeslider.version": "പതിപ്പ് {{version}}", "timeslider.saved": "സേവ് ചെയ്തത് {{month}} {{day}}, {{year}}", + "timeslider.backRevision": "ഈ പാഡിലെ ഒരു നാൾപ്പതിപ്പിലേക്ക് മടങ്ങുക", + "timeslider.forwardRevision": "ഈ പാഡിലെ അടുത്ത മാറ്റത്തിലേക്ക് പോവുക", "timeslider.dateformat": "{{month}}/{{day}}/{{year}} {{hours}}:{{minutes}}:{{seconds}}", "timeslider.month.january": "ജനുവരി", "timeslider.month.february": "ഫെബ്രുവരി", @@ -108,6 +114,7 @@ "timeslider.month.december": "ഡിസംബർ", "timeslider.unnamedauthors": "{{num}} പേരില്ലാത്ത {[plural(num) one: രചയിതാവ്, other: രചയിതാക്കൾ }}", "pad.savedrevs.marked": "ഈ നാൾപ്പതിപ്പ് സേവ് ചെയ്തിട്ടുള്ള നാൾപ്പതിപ്പായി അടയാളപ്പെടുത്തിയിരിക്കുന്നു", + "pad.savedrevs.timeslider": "സേവ് ചെയ്ത മറ്റു നാൾപ്പതിപ്പുകൾ സമയസൂചികയിൽ കാണാവുന്നതാണ്", "pad.userlist.entername": "താങ്കളുടെ പേര് നൽകുക", "pad.userlist.unnamed": "പേരില്ലാത്തവ", "pad.userlist.guest": "അതിഥി", @@ -118,6 +125,7 @@ "pad.impexp.importing": "ഇറക്കുമതി ചെയ്യുന്നു...", "pad.impexp.confirmimport": "ഒരു പ്രമാണം ഇറക്കുമതി ചെയ്യുന്നത് നിലവിലുള്ള എഴുത്തുകൾ നഷ്ടപ്പെടാനിടയാക്കും, തുടരണമെന്ന് ഉറപ്പാണോ?", "pad.impexp.convertFailed": "ഈ പ്രമാണം ഇറക്കുമതി ചെയ്യാൻ സാധിച്ചില്ല. ദയവായി മറ്റൊരു ഡോക്യുമെന്റ് ഫോർമാറ്റ് ഉപയോഗിക്കുകയോ, സ്വന്തമായി പകർത്തി ചേർക്കുകയോ ചെയ്യുക", + "pad.impexp.padHasData": "ഈ പാഡിൽ ഇതിനകം തന്നെ മാറ്റങ്ങൾ നടന്നിട്ടുള്ളതിനാൽ, നൽകിയ പ്രമാണം ഇതിലേക്ക് ചേർക്കാൻ സാധിച്ചില്ല. ദയവായി പുതിയ ഒരു പാഡിലേക്ക് ചേർക്കുക", "pad.impexp.uploadFailed": "അപ്ലോഡ് പരാജയപ്പെട്ടു. ദയവായി വീണ്ടും ശ്രമിക്കുക", "pad.impexp.importfailed": "ഇറക്കുമതി പരാജയപ്പെട്ടു", "pad.impexp.copypaste": "ദയവായി പകർത്തി ചേർക്കുക", diff --git a/src/locales/nap.json b/src/locales/nap.json index 22a5b262..c31d525a 100644 --- a/src/locales/nap.json +++ b/src/locales/nap.json @@ -5,7 +5,7 @@ "C.R." ] }, - "index.newPad": "Novo Pad", + "index.newPad": "Nuovo Pad", "index.createOpenPad": "o crià o arape nu Pad cu 'o nomme:", "pad.toolbar.bold.title": "Grassetto (Ctrl-B)", "pad.toolbar.italic.title": "Cursivo (Ctrl-I)", diff --git a/src/locales/nl.json b/src/locales/nl.json index 0aa63d8e..d58fabb9 100644 --- a/src/locales/nl.json +++ b/src/locales/nl.json @@ -7,7 +7,7 @@ ] }, "index.newPad": "Nieuw pad", - "index.createOpenPad": "Maak of open pad met de naam:", + "index.createOpenPad": "Pad maken op openen met de naam:", "pad.toolbar.bold.title": "Vet (Ctrl-B)", "pad.toolbar.italic.title": "Cursief (Ctrl-I)", "pad.toolbar.underline.title": "Onderstrepen (Ctrl-U)", @@ -45,7 +45,7 @@ "pad.settings.globalView": "Globaal overzicht", "pad.settings.language": "Taal:", "pad.importExport.import_export": "Importeren/exporteren", - "pad.importExport.import": "Upload een tekstbestand of document", + "pad.importExport.import": "Tekstbestand of document uploaden", "pad.importExport.importSuccessful": "Afgerond", "pad.importExport.export": "Huidige pad exporteren als", "pad.importExport.exportetherpad": "Etherpad", @@ -65,7 +65,7 @@ "pad.modals.unauth.explanation": "Uw rechten zijn gewijzigd terwijl u de pagina aan het bekijken was. Probeer opnieuw te verbinden.", "pad.modals.looping.explanation": "Er is een probleem opgetreden tijdens de communicatie met de synchronisatieserver.", "pad.modals.looping.cause": "Mogelijk gebruikt de server een niet compatibele firewall of proxy server.", - "pad.modals.initsocketfail": "Server is niet bereikbaar.", + "pad.modals.initsocketfail": "De server is niet bereikbaar.", "pad.modals.initsocketfail.explanation": "Het was niet mogelijk te verbinden met de synchronisatieserver.", "pad.modals.initsocketfail.cause": "Mogelijk komt dit door uw browser of internetverbinding.", "pad.modals.slowcommit.explanation": "De server reageert niet.", diff --git a/src/locales/oc.json b/src/locales/oc.json index 62625ed7..637c9061 100644 --- a/src/locales/oc.json +++ b/src/locales/oc.json @@ -92,6 +92,9 @@ "timeslider.exportCurrent": "Exportar la version actuala en :", "timeslider.version": "Version {{version}}", "timeslider.saved": "Enregistrat lo {{day}} {{month}} {{year}}", + "timeslider.playPause": "Lectura / Pausa dels contenguts del pad", + "timeslider.backRevision": "Recular d’una revision dins aqueste pad", + "timeslider.forwardRevision": "Avançar d’una revision dins aqueste pad", "timeslider.dateformat": "{{month}}/{{day}}/{{year}} {{hours}}:{{minutes}}:{{seconds}}", "timeslider.month.january": "Genièr", "timeslider.month.february": "Febrièr", diff --git a/src/locales/olo.json b/src/locales/olo.json new file mode 100644 index 00000000..e8db16f7 --- /dev/null +++ b/src/locales/olo.json @@ -0,0 +1,54 @@ +{ + "@metadata": { + "authors": [ + "Denö", + "Mashoi7", + "Ilja.mos" + ] + }, + "pad.toolbar.underline.title": "Alleviivua (Ctrl+U)", + "pad.toolbar.settings.title": "Azetukset", + "pad.colorpicker.save": "Tallenda", + "pad.colorpicker.cancel": "Hylgiä", + "pad.wrongPassword": "Peittosana oli viärin", + "pad.settings.linenocheck": "Riädynoumerot", + "pad.settings.rtlcheck": "Luve syväindö oigielpäi huruale?", + "pad.settings.language": "Kieli:", + "pad.importExport.import_export": "Tuo/Vie", + "pad.importExport.importSuccessful": "Ozavui!", + "pad.importExport.exportetherpad": "Etherpad", + "pad.importExport.exporthtml": "HTML", + "pad.importExport.exportword": "Microsoft Word", + "pad.importExport.exportpdf": "PDF", + "pad.importExport.exportopen": "ODF (Open Document Format)", + "pad.modals.userdup": "Avattu toizes ikkunas", + "pad.modals.unauth": "Ei lubua", + "pad.modals.initsocketfail": "Palvelin ei ole tavattavis.", + "pad.modals.initsocketfail.explanation": "Ei suadu yhtevytty sinhronisaciipalvelimeh.", + "pad.modals.slowcommit.explanation": "Palvelin ei vastua.", + "pad.modals.disconnected.explanation": "Yhtevys palvelimeh kavotettu", + "timeslider.toolbar.authors": "Luadijat:", + "timeslider.toolbar.authorsList": "Ei luadijoi", + "timeslider.toolbar.exportlink.title": "Vie", + "timeslider.exportCurrent": "Vie nygöine versii nimel:", + "timeslider.version": "Versii {{version}}", + "timeslider.saved": "Tallendettu {{month}} {{day}}, {{year}}", + "timeslider.dateformat": "{{month}}/{{day}}/{{year}} {{hours}}:{{minutes}}:{{seconds}}", + "timeslider.month.january": "pakkaskuudu", + "timeslider.month.february": "tuhukuudu", + "timeslider.month.march": "kevätkuudu", + "timeslider.month.april": "sulakuudu", + "timeslider.month.may": "oraskuudu", + "timeslider.month.june": "kezäkuudu", + "timeslider.month.july": "heinykuudu", + "timeslider.month.august": "elokuudu", + "timeslider.month.september": "syvyskuudu", + "timeslider.month.october": "ligakuudu", + "timeslider.month.november": "kylmykuudu", + "timeslider.month.december": "talvikuudu", + "pad.userlist.entername": "Kirjuta sinun nimi", + "pad.userlist.unnamed": "nimetöi", + "pad.userlist.guest": "Gost'u", + "pad.impexp.importbutton": "Tuo nygöi", + "pad.impexp.importing": "Tuou..." +} diff --git a/src/locales/pa.json b/src/locales/pa.json index 9e154e36..531e4ac8 100644 --- a/src/locales/pa.json +++ b/src/locales/pa.json @@ -2,7 +2,8 @@ "@metadata": { "authors": [ "Aalam", - "Babanwalia" + "Babanwalia", + "ਪ੍ਰਚਾਰਕ" ] }, "index.newPad": "ਨਵਾਂ ਪੈਡ", @@ -10,29 +11,31 @@ "pad.toolbar.bold.title": "ਗੂੜ੍ਹਾ (Ctrl-B)", "pad.toolbar.italic.title": "ਤਿਰਛਾ (Ctrl-I)", "pad.toolbar.underline.title": "ਹੇਠਾਂ-ਰੇਖਾ (Ctrl-U)", - "pad.toolbar.strikethrough.title": "ਵਿੰਨ੍ਹੋ ਵਿਨੋ", - "pad.toolbar.ol.title": "ਲੜੀਵਾਰ ਲਿਸਟ", + "pad.toolbar.strikethrough.title": "ਵਿੰਨ੍ਹੋ (Ctrl+5)", + "pad.toolbar.ol.title": "ਲੜੀਵਾਰ ਸੂਚੀ", "pad.toolbar.ul.title": "ਬਿਨ-ਲੜੀਬੱਧ ਸੂਚੀ", "pad.toolbar.indent.title": "ਹਾਸ਼ੀਏ ਤੋਂ ਪਰ੍ਹੇ (ਟੈਬ)", "pad.toolbar.unindent.title": "ਹਾਸ਼ੀਏ ਵੱਲ (ਸ਼ਿਫ਼ਟ+ਟੈਬ)", "pad.toolbar.undo.title": "ਵਾਪਸ (Ctrl-Z)", "pad.toolbar.redo.title": "ਪਰਤਾਓ (Ctrl-Y)", - "pad.toolbar.clearAuthorship.title": "ਪਰਮਾਣਕਿਤਾ ਰੰਗ ਸਾਫ਼ ਕਰੋ", + "pad.toolbar.clearAuthorship.title": "ਪਰਮਾਣਕਿਤਾ ਰੰਗ ਸਾਫ਼ ਕਰੋ (Ctrl+Shift+C)", "pad.toolbar.import_export.title": "ਵੱਖ-ਵੱਖ ਫਾਇਲ ਫਾਰਮੈਟ ਤੋਂ/ਵਿੱਚ ਇੰਪੋਰਟ/ਐਕਸਪੋਰਟ ਕਰੋ", "pad.toolbar.timeslider.title": "ਸਮਾਂ-ਲਕੀਰ", "pad.toolbar.savedRevision.title": "ਰੀਵਿਜ਼ਨ ਸੰਭਾਲੋ", "pad.toolbar.settings.title": "ਸੈਟਿੰਗ", "pad.toolbar.embed.title": "ਇਹ ਪੈਡ ਸਾਂਝਾ ਤੇ ਇੰਬੈੱਡ ਕਰੋ", "pad.toolbar.showusers.title": "ਇਹ ਪੈਡ ਉੱਤੇ ਯੂਜ਼ਰ ਵੇਖਾਓ", - "pad.colorpicker.save": "ਸਾਂਭੋ", + "pad.colorpicker.save": "ਸੰਭਾਲੋ", "pad.colorpicker.cancel": "ਰੱਦ ਕਰੋ", "pad.loading": "…ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ", + "pad.noCookie": "ਕੂਕੀਜ਼ ਨਹੀਂ ਲੱਭੀਅਾਂ। ਕਿਰਪਾ ਕਰਕੇ ਬ੍ਰਾੳੂਜ਼ਰ ਵਿੱਚ ਕੂਕੀਜ਼ ਲਾਗੂ ਕਰੋ।", "pad.passwordRequired": "ਇਹ ਪੈਡ ਦੀ ਵਰਤੋਂ ਕਰਨ ਲਈ ਤੁਹਾਨੂੰ ਪਾਸਵਰਡ ਚਾਹੀਦਾ ਹੈ", "pad.permissionDenied": "ਇਹ ਪੈਡ ਵਰਤਨ ਲਈ ਤੁਹਾਨੂੰ ਅਧਿਕਾਰ ਨਹੀਂ ਹਨ", "pad.wrongPassword": "ਤੁਹਾਡਾ ਪਾਸਵਰਡ ਗਲਤੀ ਸੀ", "pad.settings.padSettings": "ਪੈਡ ਸੈਟਿੰਗ", "pad.settings.myView": "ਮੇਰੀ ਝਲਕ", "pad.settings.stickychat": "ਹਮੇਸ਼ਾ ਸਕਰੀਨ ਉੱਤੇ ਗੱਲ ਕਰੋ", + "pad.settings.chatandusers": "ਗੱਲ-ਬਾਤ ਅਤੇ ਵਰਤੋਂਕਾਰ ਦਿਖਾਵੋ", "pad.settings.colorcheck": "ਲੇਖਕੀ ਰੰਗ", "pad.settings.linenocheck": "ਲਾਈਨ ਨੰਬਰ", "pad.settings.rtlcheck": "ਸਮੱਗਰੀ ਸੱਜੇ ਤੋਂ ਖੱਬੇ ਪੜ੍ਹਨੀ ਹੈ?", @@ -45,10 +48,11 @@ "pad.importExport.import": "ਕੋਈ ਵੀ ਟੈਕਸਟ ਫਾਇਲ ਜਾਂ ਦਸਤਾਵੇਜ਼ ਅੱਪਲੋਡ ਕਰੋ", "pad.importExport.importSuccessful": "ਸਫ਼ਲ!", "pad.importExport.export": "ਮੌਜੂਦਾ ਪੈਡ ਨੂੰ ਐਕਸਪੋਰਟ ਕਰੋ:", + "pad.importExport.exportetherpad": "ੲੈਥਰਪੈਡ", "pad.importExport.exporthtml": "HTML", "pad.importExport.exportplain": "ਸਧਾਰਨ ਟੈਕਸਟ", "pad.importExport.exportword": "ਮਾਈਕਰੋਸਾਫਟ ਵਰਡ", - "pad.importExport.exportpdf": "ਪੀਡੀਐਫ", + "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (ਓਪਨ ਡੌਕੂਮੈਂਟ ਫਾਰਮੈਟ)", "pad.importExport.abiword.innerHTML": "ਤੁਸੀਂ ਸਿਰਫ਼ ਸਾਦੀਆਂ ਲਿਖਤੀ ਜਾਂ ਐੱਚ.ਟੀ.ਐੱਮ.ਐੱਲ. ਰੂਪ-ਰੇਖਾਵਾਂ ਤੋਂ ਦਰਾਮਦ ਕਰ ਸਕਦੇ ਹੋ। ਹੋਰ ਉੱਨਤ ਦਰਾਮਦੀ ਗੁਣਾਂ ਵਾਸਤੇ ਮਿਹਰਬਾਨੀ ਕਰਕੇ <a href=\"https://github.com/ether/etherpad-lite/wiki/How-to-enable-importing-and-exporting-different-file-formats-in-Ubuntu-or-OpenSuse-or-SLES-with-AbiWord\">ਐਬੀਵਰਡ ਥਾਪੋ</a>।", "pad.modals.connected": "ਕੁਨੈਕਟ ਹੈ।", @@ -88,8 +92,11 @@ "timeslider.toolbar.authorsList": "ਕੋਈ ਲੇਖਕ ਨਹੀਂ", "timeslider.toolbar.exportlink.title": "ਐਕਸਪੋਰਟ", "timeslider.exportCurrent": "ਮੌਜੂਦਾ ਵਰਜਨ ਇੰਝ ਐਕਸਪੋਰਟ ਕਰੋ:", - "timeslider.version": "ਵਰਜਨ {{version}}", + "timeslider.version": "ਵਰਜ਼ਨ {{version}}", "timeslider.saved": "{{day}} {{month}} {{year}} ਨੂੰ ਸੰਭਾਲਿਆ", + "timeslider.playPause": "ਪੈਡ ਸਮੱਗਰੀ ਚਲਾਓ / ਵਿਰਾਮ ਕਰੋ", + "timeslider.backRevision": "ਇਸ ਪੈਡ ਵਿੱਚ ਪਿਛਲੇ ਰੀਵਿਜ਼ਨ ਤੇ ਜਾਓ", + "timeslider.forwardRevision": "ਇਸ ਪੈਡ ਵਿੱਚ ਅਗਲੇ ਰੀਵਿਜ਼ਨ ਤੇ ਜਾਓ", "timeslider.dateformat": "{{day}}/{{month}}/{{year}} {{hours}}:{{minutes}}:{{seconds}}", "timeslider.month.january": "ਜਨਵਰੀ", "timeslider.month.february": "ਫ਼ਰਵਰੀ", @@ -118,5 +125,5 @@ "pad.impexp.uploadFailed": "ਅੱਪਲੋਡ ਲਈ ਫੇਲ੍ਹ ਹੈ, ਫੇਰ ਕੋਸ਼ਿਸ਼ ਕਰੋ ਜੀ।", "pad.impexp.importfailed": "ਇੰਪੋਰਟ ਫੇਲ੍ਹ ਹੈ", "pad.impexp.copypaste": "ਕਾਪੀ ਕਰੋ ਚੇਪੋ ਜੀ", - "pad.impexp.exportdisabled": "{{type}} ਰੂਪ-ਰੇਖਾ ਵਜੋਂ ਬਰਾਮਦ ਕਰਨਾ ਬੰਦ ਹੈ। ਵੇਰਵੇ ਵਾਸਤੇ ਮਿਹਰਬਾਨੀ ਕਰਕੇ ਆਪਣੇ ਸਿਸਟਮ ਦੇ ਪ੍ਰਬੰਧਕ ਨਾਲ਼ ਰਾਬਤਾ ਬਣਾਉ।" + "pad.impexp.exportdisabled": "{{type}} ਫਾਰਮੈਟ ਵਜੋਂ ਬਰਾਮਦ ਕਰਨਾ ਬੰਦ ਹੈ। ਵੇਰਵੇ ਵਾਸਤੇ ਆਪਣੇ ਸਿਸਟਮ ਦੇ ਪਰਬੰਧਕ ਨਾਲ ਸੰਪਰਕ ਕਰੋ।" } diff --git a/src/locales/pt-br.json b/src/locales/pt-br.json index 59d679c8..e356041f 100644 --- a/src/locales/pt-br.json +++ b/src/locales/pt-br.json @@ -13,7 +13,9 @@ "Rodrigo codignoli", "Webysther", "Fasouzafreitas", - "Lpagliari" + "Lpagliari", + "Walesson", + "Cainamarques" ] }, "index.newPad": "Nova Nota", @@ -104,6 +106,9 @@ "timeslider.exportCurrent": "Exportar a versão atual em formato:", "timeslider.version": "Versão {{version}}", "timeslider.saved": "Salvo em {{day}} de {{month}} de {{year}}", + "timeslider.playPause": "Reproduzir / Pausar conteúdo no Pad", + "timeslider.backRevision": "Voltar a uma revisão anterior neste Pad", + "timeslider.forwardRevision": "Ir a uma revisão posterior neste Pad", "timeslider.dateformat": "{{day}}/{{month}}/{{year}} {{hours}}:{{minutes}}:{{seconds}}", "timeslider.month.january": "Janeiro", "timeslider.month.february": "Fevereiro", diff --git a/src/locales/pt.json b/src/locales/pt.json index 473980fd..b5cbe1d8 100644 --- a/src/locales/pt.json +++ b/src/locales/pt.json @@ -7,7 +7,8 @@ "Waldir", "Imperadeiro98", "Macofe", - "Ti4goc" + "Ti4goc", + "Cainamarques" ] }, "index.newPad": "Nova Nota", @@ -106,7 +107,7 @@ "pad.impexp.importbutton": "Importar agora", "pad.impexp.importing": "Importando...", "pad.impexp.confirmimport": "A importação de um ficheiro irá substituir o texto atual do pad. Tem certeza que deseja continuar?", - "pad.impexp.padHasData": "Não fomos capazes de importar este ficheiro porque esta Almofada já tinha alterações, consulte importar para um novo bloco", + "pad.impexp.padHasData": "Não fomos capazes de importar este arquivo porque este Pad já tinha alterações, por favor importe para um novo pad", "pad.impexp.uploadFailed": "O upload falhou. Por favor, tente novamente", "pad.impexp.importfailed": "A importação falhou", "pad.impexp.copypaste": "Por favor, copie e cole" diff --git a/src/locales/ro.json b/src/locales/ro.json index 85436dd0..a73bfafc 100644 --- a/src/locales/ro.json +++ b/src/locales/ro.json @@ -3,7 +3,9 @@ "authors": [ "Hedwig", "ImGelu", - "Minisarm" + "Minisarm", + "Strainu", + "Wintereu" ] }, "index.newPad": "Pad nou", @@ -15,8 +17,12 @@ "pad.toolbar.ol.title": "Listă ordonată (Ctrl+Shift+N)", "pad.toolbar.ul.title": "Listă neordonată (Ctrl+Shift+L)", "pad.toolbar.undo.title": "Anulează (Ctrl+Z)", + "pad.toolbar.redo.title": "Refă (Ctrl+Y)", + "pad.toolbar.clearAuthorship.title": "Curăță culorile autorilor (Ctrl+Shift+C)", + "pad.toolbar.import_export.title": "Importă/Exportă din/în diferite formate", "pad.toolbar.savedRevision.title": "Salvează revizia", "pad.toolbar.settings.title": "Setări", + "pad.toolbar.showusers.title": "Arată utilizatorii de pe acest pad", "pad.colorpicker.save": "Salvează", "pad.colorpicker.cancel": "Anulează", "pad.loading": "Se încarcă...", @@ -26,9 +32,12 @@ "pad.settings.padSettings": "Setări pentru Pad", "pad.settings.stickychat": "Chat-ul întotdeauna pe ecran", "pad.settings.chatandusers": "Afișează Chat-ul și Utilizatorii", + "pad.settings.colorcheck": "Culorile autorilor", + "pad.settings.linenocheck": "Numere de linie", "pad.settings.fontType": "Tipul fontului:", "pad.settings.globalView": "Vedere generală", "pad.settings.language": "Limbă:", + "pad.importExport.import_export": "Import/Export", "pad.importExport.import": "Încarcă orice fișier text sau document", "pad.importExport.importSuccessful": "Succes!", "pad.importExport.export": "Exportă pad-ul curent ca:", @@ -39,9 +48,10 @@ "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", "pad.modals.connected": "Conectat.", - "pad.modals.reconnecting": "Se reconectează la pad-ul tău..", + "pad.modals.reconnecting": "Se reconectează la pad-ul dumneavoastră..", "pad.modals.forcereconnect": "Forțează reconectarea", - "pad.modals.userdup.advice": "Reconectează pentru a folosi această fereastră în schimb", + "pad.modals.userdup": "Deschis în altă fereastră", + "pad.modals.userdup.advice": "Reconectați-vă dacă doriți să utilizați această fereastră.", "pad.modals.unauth": "Nu ești autorizat", "pad.modals.initsocketfail": "Serverul nu este disponibil.", "pad.modals.initsocketfail.explanation": "Nu s-a putut conecta la serverul de sincronizare.", @@ -53,12 +63,12 @@ "pad.share": "Distribuie acest pad", "pad.share.readonly": "Doar în citire", "pad.share.link": "Legătură", - "pad.share.emebdcode": "Încorporează URL-ul", + "pad.share.emebdcode": "Adresa URL încorporată", "pad.chat": "Chat", "pad.chat.title": "Deschide chat-ul pentru acest pad.", "pad.chat.loadmessages": "Încarcă mai multe mesaje", "timeslider.toolbar.returnbutton": "Înapoi la pad", - "timeslider.toolbar.authors": "Aurori:", + "timeslider.toolbar.authors": "Autori:", "timeslider.toolbar.authorsList": "Niciun autor", "timeslider.toolbar.exportlink.title": "Exportă", "timeslider.exportCurrent": "Exportă versiunea curentă ca:", @@ -77,6 +87,12 @@ "timeslider.month.october": "octombrie", "timeslider.month.november": "noiembrie", "timeslider.month.december": "decembrie", - "pad.userlist.entername": "Introdu numele tău", - "pad.userlist.unnamed": "fără nume" + "pad.userlist.entername": "Introduceți numele dumneavoastră", + "pad.userlist.unnamed": "fără nume", + "pad.userlist.guest": "Oaspete", + "pad.userlist.deny": "Respinge", + "pad.userlist.approve": "Aprobă", + "pad.impexp.importbutton": "Importă acum", + "pad.impexp.importing": "Importare...", + "pad.impexp.importfailed": "Import eșuat" } diff --git a/src/locales/ru.json b/src/locales/ru.json index 9a17dda7..2d92b1b6 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -5,7 +5,8 @@ "DCamer", "Eleferen", "Okras", - "Volkov" + "Volkov", + "Nzeemin" ] }, "index.newPad": "Создать", @@ -96,6 +97,9 @@ "timeslider.exportCurrent": "Экспортировать текущую версию как:", "timeslider.version": "Версия {{version}}", "timeslider.saved": "Сохранено {{day}}.{{month}}.{{year}}", + "timeslider.playPause": "Воспроизведение / Пауза содержимого документа", + "timeslider.backRevision": "Назад на одну версию документа", + "timeslider.forwardRevision": "Вперёд на одну версию документа", "timeslider.dateformat": "{{month}}/{{day}}/{{year}} {{hours}}:{{minutes}}:{{seconds}}", "timeslider.month.january": "январь", "timeslider.month.february": "февраль", @@ -111,6 +115,7 @@ "timeslider.month.december": "декабрь", "timeslider.unnamedauthors": "{{num}} {[plural(num) one: безымянный автор, few: безымянных автора, many: безымянных авторов, other: безымянных авторов]}", "pad.savedrevs.marked": "Эта версия теперь помечена как сохраненная", + "pad.savedrevs.timeslider": "Вы можете увидеть сохранённые версии на шкале времени", "pad.userlist.entername": "Введите ваше имя", "pad.userlist.unnamed": "безымянный", "pad.userlist.guest": "Гость", @@ -121,8 +126,9 @@ "pad.impexp.importing": "Импортирование…", "pad.impexp.confirmimport": "Импорт файла перезапишет текущий текст. Вы уверены, что вы хотите продолжить?", "pad.impexp.convertFailed": "Не удалось импортировать этот файл. Пожалуйста, используйте другой формат или скопируйте вручную", + "pad.impexp.padHasData": "Не получилось импортировать этот файл, потому что этот документ уже имеет изменения, пожалуйста, импортируйте в новый документ", "pad.impexp.uploadFailed": "Загрузка не удалась, пожалуйста, попробуйте ещё раз", - "pad.impexp.importfailed": "Ошибка при импортировании", + "pad.impexp.importfailed": "Ошибка при импорте", "pad.impexp.copypaste": "Пожалуйста, скопируйте", "pad.impexp.exportdisabled": "Экспорт в формате {{type}} отключен. Для подробной информации обратитесь к системному администратору." } diff --git a/src/locales/shn.json b/src/locales/shn.json new file mode 100644 index 00000000..434ef495 --- /dev/null +++ b/src/locales/shn.json @@ -0,0 +1,126 @@ +{ + "@metadata": { + "authors": [ + "Saosukham" + ] + }, + "index.newPad": "ၽႅတ်ႉမႂ်ႇ", + "index.createOpenPad": "ဢမ်ႇၼၼ် ၶူင်မႂ်ႇ/ပိုတ်ႇၽႅတ်ႉၵိုၵ်းၸိုဝ်ႈ", + "pad.toolbar.bold.title": "လမ် (Ctrl+B)", + "pad.toolbar.italic.title": "ၵိူင်း (Ctrl+I)", + "pad.toolbar.underline.title": "ထတ်းထႅဝ်တႂ်ႈ (Ctrl+U)", + "pad.toolbar.strikethrough.title": "လတ်းၵၢင် (Ctrl+5)", + "pad.toolbar.ol.title": "ၵုမ်းသဵၼ်ႈ (Ctrl+Shift+N)", + "pad.toolbar.ul.title": "ဢမ်ႇၵုမ်းသဵၼ်ႈ (Ctrl+Shift+L)", + "pad.toolbar.indent.title": "ၶၼ်ႈ (TAB)", + "pad.toolbar.unindent.title": "ၶၼ်ႈႁၢင်ႇ (Shift+TAB)", + "pad.toolbar.undo.title": "ၶိုၼ်းဢမ်ႇႁဵတ်း (Ctrl+Z)", + "pad.toolbar.redo.title": "ၶိုၼ်းႁဵတ်း (Ctrl+Y)", + "pad.toolbar.clearAuthorship.title": "သၢင်းလၢင်းပႅတ်ႈ သီဢၼ်မီးဝႆႉၵဝ်ႇ (Ctrl+Shift+C)", + "pad.toolbar.import_export.title": "သူင်ႇၶဝ်ႈ/သူင်ႇဢွၵ်ႇ တမ်ႈတီႈ/ထိုင် ၾၢႆႇၾေႃးမိတ်ႉ ဢၼ်ဢမ်ႇမိူၼ်ၵၼ်ၸိူဝ်းၼၼ်ႉ", + "pad.toolbar.timeslider.title": "ၶၢဝ်းယၢမ်းထွၵ်ႇလၢႆႈ", + "pad.toolbar.savedRevision.title": "သိမ်းလွင်ႈၶိုၼ်းမႄး", + "pad.toolbar.settings.title": "ပိူင်ႁႅၼ်း", + "pad.toolbar.embed.title": "ၽႄပၼ်ၽႅတ်ႉဢၼ်ၼႆႉသေ ၽိူမ်ႉပၼ်", + "pad.toolbar.showusers.title": "ၼႄပၼ်ၵေႃႉၸႂ်ႉ တီႈၼိူဝ်ၽႅတ်ႉၼႆႉ", + "pad.colorpicker.save": "ၵဵပ်းသိမ်း", + "pad.colorpicker.cancel": "ဢမ်ႇႁဵတ်း", + "pad.loading": "တိုၵ်ႉလူတ်ႇ", + "pad.noCookie": "ၶုၵ်းၶီး ဢမ်ႇႁၼ်လႆႈ။ ၶႅၼ်းတေႃႈ ၶႂၢင်းပၼ် ၶုၵ်းၶီး တီႈၼႂ်း ပရၢဝ်ႇသႃႇၸဝ်ႈၵဝ်ႇ", + "pad.passwordRequired": "တႃႇၶဝ်ႈတီႈၽႅတ်ႉၼႆႉ ၸဝ်ႈၵဝ်ႇ လူဝ်ႇမီး မၢႆလပ်ႉ", + "pad.permissionDenied": "ၸဝ်ႈၵဝ်ႇ ဢမ်ႇမီးၶေႃႈၶႂၢင်ႉ တႃႇၶဝ်ႈၼႂ်းၽႅတ်ႉၼႆႉ", + "pad.wrongPassword": "မၢႆလပ်ႉၸဝ်ႈၵဝ်ႇ ၽိတ်း", + "pad.settings.padSettings": "ပိူင်သၢင်ႈၽႅတ်ႉ", + "pad.settings.myView": "ဝိဝ်းၵဝ်", + "pad.settings.stickychat": "ၶျၢတ်ႉၼိူဝ်ၼႃႈၽိဝ် တႃႇသေႇ", + "pad.settings.chatandusers": "ၼႄၶျၢတ်ႉဢိၵ်ႇၵေႃႉၸႂ်ႉ", + "pad.settings.colorcheck": "သီၸိူဝ်းမီးဝႆႉၵဝ်ႇၵဝ်ႇ", + "pad.settings.linenocheck": "တူဝ်လိၵ်ႈႁေႈႁၢႆး", + "pad.settings.rtlcheck": "လူၵႂၢမ်းၼၢမ်း တႄႇၶႂႃတေႃႇသၢႆႉ", + "pad.settings.fontType": "ၾွၼ်ႉတူဝ်လိၵ်ႈ", + "pad.settings.globalView": "ဝိဝ်းတင်းလူၵ်ႈ", + "pad.settings.language": "ၽႃႇသႃႇၵႂၢမ်း:", + "pad.importExport.import_export": "သူင်ႇၶဝ်/သူင်ႇဢွၵ်ႇ", + "pad.importExport.import": "လူတ်ႇၶိုၼ်ႈ ၾၢႆႇလိၵ်ႈၵမ်ႈၽွင်ႈ ဢမ်ႇၼၼ် ပွင်ႈလိၵ်ႈ", + "pad.importExport.importSuccessful": "ဢွင်ႇယဝ်ႉ!", + "pad.importExport.export": "ဢဝ်ဢွၵ်ႇၽႅတ်ႉဢၼ်ပိူၼ်ႈသူင် ၸိူင်ႉၼင်ႇ:", + "pad.importExport.exportetherpad": "ၽႅတ်ႉပဝ်ႇ", + "pad.importExport.exporthtml": "HTML", + "pad.importExport.exportplain": "တူဝ်လိၵ်ႈပဝ်ႇ", + "pad.importExport.exportword": "မၢႆႇၶရူဝ်ႇသွပ်ႉဝၢတ်ႉ", + "pad.importExport.exportpdf": "ၽီႇတီႇဢႅပ်ႉၾ်", + "pad.importExport.exportopen": "ဢူဝ်တီႇဢႅပ်ႉၾ် (Open Document Format)", + "pad.importExport.abiword.innerHTML": "ၸဝ်ႈၵဝ်ႇၸၢင်ႈလုၵ်ႉတီႈ လိၵ်ႈပဝ်ႇသေ သူင်ႇၶဝ်ႈၵႂႃႇ ဢမ်ႇၼၼ် ပိူင်လၢႆႈ HTML. ပုၼ်ႈတႃႇ ၸိူဝ်းပိူင်မႂ်ႇ ဢၼ်သူင်ႇၶဝ်ႈမႃးၼၼ်ႉ ၶွပ်ႈၸႂ်သေ <a href=\"https://github.com/ether/etherpad-lite/wiki/How-to-enable-importing-and-exporting-different-file-formats-in-Ubuntu-or-OpenSuse-or-SLES-with-AbiWord\">install abiword</a>.", + "pad.modals.connected": "ၵွင်ႉသၢၼ်ယဝ်ႉ", + "pad.modals.reconnecting": "ၶိုၼ်းၵွင်ႉသၢၼ်ၸူး ၽႅတ်ႉၸဝ်ႈၵဝ်ႇယူႇ", + "pad.modals.forcereconnect": "တဵၵ်းၸႂ်ႉ ၶိုၼ်းၵွင်ႉသၢၼ်", + "pad.modals.userdup": "ပိုတ်ႇတမ်ႈတီႈ ၼႃႈတူမႂ်ႇ", + "pad.modals.userdup.explanation": "တမ်ႈတီႈၼႂ်းၶွမ်းတၢင်ႇဢၼ်ၼၼ်ႉ ၽႅတ်ႉဢၼ်ၼႆႉ လႅပ်ႉပိုတ်ႇဝႆႉ တမ်ႈတီႈ ပရၢဝ်ႇသႃႇတၢင်ႇတီႈယူႇ", + "pad.modals.userdup.advice": "ၶိုၼ်းၵွင်ႉသၢၼ်တၢင် တမ်ႈတီႈ ဝိၼ်းတူဝ်းၼႆႉ", + "pad.modals.unauth": "ဢမ်ႇမီးသုၼ်ႇႁဵတ်း", + "pad.modals.unauth.explanation": "ၽွင်းၼႄၼႃႈလိၵ်ႈၼႆႉယူႇၼၼ်ႉ ၶေႃႈၶႂၢင်းၸဝ်ႈၵဝ်ႇ လႅၵ်ႈလၢႆႉယဝ်ႉယဝ်ႉ။ ၶတ်းၸႂ် ၶိုၼ်းၵွင်ႉသၢၼ်တႅင်ႈ", + "pad.modals.looping.explanation": "ၸိူဝ်းၼႆႉ မီးဝႆႉပၼ်ႁႃ သၢႆတိတ်းတေႃႇ ၵိုၵ်းလူၺ်ႈ သႃႇဝႃႇ ဢၼ်ၸၼ်ထိင်းဝႆႉ", + "pad.modals.looping.cause": "သင်ပဵၼ်လႆႈ မႂ်းၶဝ်ႈၵွင်ႉသၢၼ် ၾၢႆးယႃးဝေႃး ဢမ်ႇၼၼ် ပရွၵ်ႉသီႇ ဢၼ်ဢမ်ႇငၢမ်ႇၵၼ်", + "pad.modals.initsocketfail": "သႃႇဝႃႇ ဢမ်ႇၵွင်ႉလႆႈ", + "pad.modals.initsocketfail.explanation": "ဢမ်ႇၵွင်ႉၸူးလႆႈ သႃႇဝႃႇဢၼ်ၸၼ်ထိင်းဝႆႉ", + "pad.modals.initsocketfail.cause": "ၼႆႉပဵၼ်ပၼ်ႁႃၶိုၵ်ႉလူင် ၵိုၵ်းလူၺ်ႈ ပရၢဝ်ႇသႃႇၸဝ်ႈၵဝ်ႇ ဢမ်ႇၼၼ် သၢႆၼႅင်ႈၸဝ်ႈၵဝ်ႇ", + "pad.modals.slowcommit.explanation": "သႃႇဝႃႇ ဢမ်ႇတွပ်ႇပၼ်", + "pad.modals.slowcommit.cause": "ၼႆႉပူပ်ႉၺႃး ပၼ်ႁႃ ၵိုၵ်းလူၺ်ႈ သၢႆၼႅင်ႈၵွင်ႉသၢၼ်", + "pad.modals.badChangeset.explanation": "ၶေႃႈထတ်း ဢၼ်ၸဝ်ႈၵဝ်ႇႁဵတ်းၼၼ်ႉ မၼ်းဢမ်ႇႁူမ်ႈၶဝ်ႈၶႂၢင်ႇ ၸွမ်းၼင်ႇ သႃႇဝႃႇဢၼ် ၸၼ်ထိင်းဝႆႉ", + "pad.modals.corruptPad.explanation": "ၽႅတ်ႉဢၼ်ၸဝ်ႈၵဝ်ႇပေႃႉၼၼ်ႉ ၶဝ်ႈၽိတ်းဝႆႉ", + "pad.modals.corruptPad.cause": "ဢၼ်ၼႆႉ သႃႇဝႃႇဢၼ်ၸၼ်ထိင်းမၼ်း ၽိတ်းဝႆႉ ဢမ်ႇၼၼ် ဢမ်ႇမုင်ႈမွင်းသေ ၽိတ်းပိူင်ႈဝႆႉယဝ်ႉ။ ၶႅၼ်းတေႃႈ ၵပ်းသိုပ်ႇတမ်ႈတီႈ ၽူႈၵုမ်းၵၢၼ်.", + "pad.modals.deleted": "ယႃႉ", + "pad.modals.deleted.explanation": "ၽႅတ်ႉဢၼ်ၼႆႉ ၶၢႆႉပႅတ်ႈယဝ်ႉ", + "pad.modals.disconnected": "ၸဝ်ႈၵဝ်ႇ ဢမ်ႇၵွင်ႉသၢၼ်ဝႆႉ", + "pad.modals.disconnected.explanation": "လွင်ႈၵွင်ႉသၢၼ် ၵႂႃႇၸူးသႃႇဝႃႇၼၼ်ႉ ႁၢႆဝႆႉ", + "pad.modals.disconnected.cause": "သႃႇဝႃႇတေဢမ်ႇၸၢင်ႈယိပ်းတိုဝ်း။ တႃႇႁႂ်ႈသိုပ်ႇပဵၼ်ၵၢၼ်ၵႂႃႇၼၼ်ႉ ၶႅၼ်းတေႃႈ ပွင်ႇၶၢဝ်ႇ တမ်ႈတီႈ ၽူႈၵုမ်းၵၢၼ်", + "pad.share": "ၽႄၽႅတ်ႉၼႆႉ", + "pad.share.readonly": "လူလၢႆလၢႆ", + "pad.share.link": "ၵွင်ႉ", + "pad.share.emebdcode": "သႂ်ႇ URL", + "pad.chat": "ၶျၢတ်ႉ", + "pad.chat.title": "ပိုတ်ႇၶျၢတ်ႉ တႃႇၽႅတ်ႉၼႆႉ", + "pad.chat.loadmessages": "လူတ်ႇၶေႃႈၶၢဝ်ႇ လိူဝ်", + "timeslider.pageTitle": "{{appTitle}} ၶၢဝ်းယၢမ်းထေႃပူၼ်ႉ", + "timeslider.toolbar.returnbutton": "ၶိုၼ်းၵႂႃႇၸူး ၽႅတ်ႉ", + "timeslider.toolbar.authors": "ၽူႈတႅမ်ႈလိၵ်ႈ", + "timeslider.toolbar.authorsList": "ဢမ်ႇၸႂ်ႈ ၽူႈတႅမ်ႈလိၵ်ႈ", + "timeslider.toolbar.exportlink.title": "သူင်ႇဢွၵ်ႇ", + "timeslider.exportCurrent": "သူင်ႇဢွၵ်ႇ လုၼ်ႈပၢၼ်မႂ်ႇ ၼင်ႇ:", + "timeslider.version": "လုၼ်ႈ {{version}}", + "timeslider.saved": "သိမ်းယဝ်ႉ{{month}} {{day}}, {{year}}", + "timeslider.playPause": "လဵၼ်ႈလင်/ၵိုတ်းသဝ်း ၵႂၢမ်းၼၢမ်း ၽႅတ်ႉ", + "timeslider.backRevision": "ႁူၼ်လင် ၶိုၼ်းမႄး ၽႅတ်ႉၼႆႉ", + "timeslider.forwardRevision": "ၵႂႃႇၼႃႈ ၶိုၼ်းမႄး ၽႅတ်ႉၼႆႉ", + "timeslider.dateformat": "{{month}}/{{day}}/{{year}} {{hours}}:{{minutes}}:{{seconds}}", + "timeslider.month.january": "ၵျၼ်ႇၼိဝ်ႇရီႇ", + "timeslider.month.february": "ၾႅပ်ႇဝႃႇရီႇ", + "timeslider.month.march": "မၢတ်ႉၶျ်", + "timeslider.month.april": "ဢေႇပရႄႇ", + "timeslider.month.may": "မေႇ", + "timeslider.month.june": "ၵျုၼ်ႇ", + "timeslider.month.july": "ၵျူႇလၢႆႇ", + "timeslider.month.august": "ဢေႃးၵၢတ်ႉ", + "timeslider.month.september": "သႅပ်ႉထိမ်ႇပႃႇ", + "timeslider.month.october": "ဢွၵ်ႇထူဝ်ႇပႃႇ", + "timeslider.month.november": "ၼူဝ်ႇဝႅမ်ႇပႃႇ", + "timeslider.month.december": "တီႇသႅမ်ႇပႃႇ", + "timeslider.unnamedauthors": "{{num}} ဢမ်ႇသႂ်ႇၸိုဝ်ႈ {[plural(num) ၼိုင်ႈ: ၽူႈတႅမ်ႈလိၵ်ႈ, တၢင်ႇၸိူဝ်း: ၽူႈတႅမ်ႈလိၵ်ႈၶဝ် ]}", + "pad.savedrevs.marked": "ဢၼ်မႄးဝႆႉၼႆႉ ယၢမ်းလဵဝ် ၶိုၼ်းမႄး ၵဵပ်းသိမ်းဝႆႉ ၸိူင်ႉၼင်ႇဢၼ်ၼိုင်ႈ", + "pad.savedrevs.timeslider": "ၸဝ်ႈၵဝ်ႇတေတူၺ်းလႆႈ ဢၼ်ၶိုၼ်းမႄးသိမ်းဝႆႉၼၼ်ႉယူႇ ပေႃးၶဝ်ႈတူၺ်းတီႈ ၶၢဝ်းယၢမ်းထေႃပူၼ်ႉ", + "pad.userlist.entername": "ပေႃႇသႂ်ႇပၼ် ၸိုဝ်ႈၽူႈၸႂ်ႉတိုဝ်း ၸဝ်ႈၵဝ်ႇ", + "pad.userlist.unnamed": "ဢမ်ႇသႂ်ႇၸိုဝ်ႈ", + "pad.userlist.guest": "ၶႅၵ်ႇ", + "pad.userlist.deny": "ထဵင်", + "pad.userlist.approve": "ၵမ်ႉထႅမ်", + "pad.editbar.clearcolors": "လၢင်ႉပႅတ်ႈသီၵဝ်ႇ တမ်ႈတီႈ ၼိူဝ်ၶေႃႈလိၵ်ႈတင်းသဵင်ႈ", + "pad.impexp.importbutton": "သူင်ႇၶဝ်ႈယၢမ်းလဵဝ်", + "pad.impexp.importing": "တိုၵ်ႉသူင်ႇၶဝ်ႈယူႇ", + "pad.impexp.confirmimport": "ဢဝ်ၾၢႆႇၶဝ်ႈမႃးၼႆႉ တေမႃး တဵင်သႂ်ႇ လိၵ်ႈဢၼ်မီးဝႆႉ တီႈၼႂ်းၽႅတ်ႉၼႆႉယဝ်ႉ။ ၸွင်ႇၸဝ်ႈၵဝ်ႇ လပ်ႉလွင်းဝႃႈ ၸဝ်ႈၵဝ်ႇ တေၶႂ်ႈ ငူပ်ႉငိႁိုဝ်?", + "pad.impexp.convertFailed": "ႁဝ်းဢမ်ႇၸၢင်းႁဵတ်းႁိုဝ် ဢဝ်ၾၢႆႇၼႆႉ သူင်ႇၶဝ်ႈၵႂႃႇ။ ၶႅၼ်းတေႃႈ ၸႂ်ႉတိုဝ်း ၶေႃႈလိၵ်ႈ ဢၼ်ပႅၵ်ႇပိူင်ႈၵၼ် ဢမ်ႇၼၼ် ၵူးထုတ်ႇဝႆႉလႄႈ", + "pad.impexp.padHasData": "ႁဝ်းဢမ်ႇၸၢင်းႁဵတ်းႁိုဝ် ဢဝ်ၾၢႆႇၼႆႉ သူင်ႇၶဝ်ႈၵႂႃႇ ၵွပ်ႈပိူဝ်ႈဝႃႈ ၽႅတ်ႉဢၼ်ၼႆႉ မၼ်းလႅၵ်ႈလၢႆႈၵႂႃႇယဝ်ႉယဝ်ႈ။ ၶွပ်ႈၸႂ် သူင်ႇၶႂ်ႈၼႂ်း ၽႅတ်ႉမႂ်ႇလႄႈ", + "pad.impexp.uploadFailed": "ဢၼ်လူတ်ႇၶိုၼ်ႈ ၶၢတ်ႇတူၵ်းယဝ်ႉ။ ၶႅၼ်းတေႃႈ ၶတ်းၸႂ်ထႅင်ႈ", + "pad.impexp.importfailed": "ဢၼ်သူင်ႇၶဝ်ႈ ၶၢတ်ႇတူၵ်းယဝ်ႉ", + "pad.impexp.copypaste": "ၶႅၼ်းတေႃႈ ၵူးသေ ဢဝ်ဝႆႉတ", + "pad.impexp.exportdisabled": "ၸိူင်ႉၼင်ႇ {{type}} သူင်ႇဢွၵ်ႇ ၾေႃးမဵတ်ႉ ၼၼ်ႉ ဢမ်ႇၸၢင်ႈ။ ၶႅၼ်းတေႃႈ ၵပ်းသိုပ်ႇ ၽူႈၵုမ်းၵၢၼ်ၸၢၵ်ႈ တႃႇႁူဝ်ယွႆႈမၼ်းတ" +} diff --git a/src/locales/sk.json b/src/locales/sk.json index 02c5c978..445adc00 100644 --- a/src/locales/sk.json +++ b/src/locales/sk.json @@ -3,7 +3,8 @@ "authors": [ "Teslaton", "Kusavica", - "Rudko" + "Rudko", + "Mark" ] }, "index.newPad": "Nový Pad", @@ -11,14 +12,14 @@ "pad.toolbar.bold.title": "Tučné (Ctrl-B)", "pad.toolbar.italic.title": "Kurzíva (Ctrl-I)", "pad.toolbar.underline.title": "Podčiarknuté (Ctrl-U)", - "pad.toolbar.strikethrough.title": "Prečiarknuté", - "pad.toolbar.ol.title": "Číslovaný zoznam", - "pad.toolbar.ul.title": "Odrážkový zoznam", + "pad.toolbar.strikethrough.title": "Prečiarknuté (Ctrl+5)", + "pad.toolbar.ol.title": "Usporiadaný zoznam (Ctrl+Shift+N)", + "pad.toolbar.ul.title": "Nezoradený zoznam (Ctrl+Shift+L)", "pad.toolbar.indent.title": "Zväčšiť odsadenie (TAB)", "pad.toolbar.unindent.title": "Zmenšiť odsadenie (Shift+TAB)", "pad.toolbar.undo.title": "Späť (Ctrl-Z)", "pad.toolbar.redo.title": "Znova (Ctrl-Y)", - "pad.toolbar.clearAuthorship.title": "Odstrániť farby autorstva", + "pad.toolbar.clearAuthorship.title": "Odstrániť farby autorstva (Ctrl+Shift+C)", "pad.toolbar.import_export.title": "Import/export z/do rôznych formátov súborov", "pad.toolbar.timeslider.title": "Časová os", "pad.toolbar.savedRevision.title": "Uložiť revíziu", @@ -46,6 +47,7 @@ "pad.importExport.import": "Nahrať ľubovoľný textový súbor alebo dokument", "pad.importExport.importSuccessful": "Import úspešný!", "pad.importExport.export": "Exportovať aktuálny Pad ako:", + "pad.importExport.exportetherpad": "Etherpad", "pad.importExport.exporthtml": "HTML", "pad.importExport.exportplain": "Čistý text", "pad.importExport.exportword": "Microsoft Word", diff --git a/src/locales/sq.json b/src/locales/sq.json index 6374ea62..a465cfaa 100644 --- a/src/locales/sq.json +++ b/src/locales/sq.json @@ -1,10 +1,11 @@ { "@metadata": { "authors": [ - "Besnik b" + "Besnik b", + "Kosovastar" ] }, - "index.newPad": "Bllok i Ri", + "index.newPad": "Bllok i ri", "index.createOpenPad": "ose krijoni/hapni një Bllok me emrin:", "pad.toolbar.bold.title": "Të trasha (Ctrl-B)", "pad.toolbar.italic.title": "Të pjerrëta (Ctrl-I)", @@ -12,7 +13,7 @@ "pad.toolbar.strikethrough.title": "Hequrvije (Ctrl+5)", "pad.toolbar.ol.title": "Listë e renditur (Ctrl+Shift+N)", "pad.toolbar.ul.title": "Listë e parenditur (Ctrl+Shift+L)", - "pad.toolbar.indent.title": "Brendazi (TAB)", + "pad.toolbar.indent.title": "E dhëmbëzuar (TAB)", "pad.toolbar.unindent.title": "Jashtazi (Shift+TAB)", "pad.toolbar.undo.title": "Zhbëje (Ctrl-Z)", "pad.toolbar.redo.title": "Ribëje (Ctrl-Y)", @@ -20,7 +21,7 @@ "pad.toolbar.import_export.title": "Importoni/Eksportoni nga/në formate të tjera kartelash", "pad.toolbar.timeslider.title": "Rrjedha kohore", "pad.toolbar.savedRevision.title": "Ruaje Rishikimin", - "pad.toolbar.settings.title": "Rregullime", + "pad.toolbar.settings.title": "Parametrat", "pad.toolbar.embed.title": "Ndajeni me të tjerët dhe Trupëzojeni këtë bllok", "pad.toolbar.showusers.title": "Shfaq përdoruesit në këtë bllok", "pad.colorpicker.save": "Ruaje", @@ -39,11 +40,12 @@ "pad.settings.fontType.normal": "Normale", "pad.settings.fontType.monospaced": "Monospace", "pad.settings.globalView": "Pamje Globale", - "pad.settings.language": "Gjuhë:", + "pad.settings.language": "Gjuha:", "pad.importExport.import_export": "Import/Eksport", "pad.importExport.import": "Ngarkoni cilëndo kartelë teksti ose dokument", "pad.importExport.importSuccessful": "Me sukses!", "pad.importExport.export": "Eksportojeni bllokun e tanishëm si:", + "pad.importExport.exportetherpad": "Etherpad", "pad.importExport.exporthtml": "HTML", "pad.importExport.exportplain": "Tekst të thjeshtë", "pad.importExport.exportword": "Microsoft Word", @@ -57,7 +59,7 @@ "pad.modals.userdup.explanation": "Ky bllok duket se gjendet i hapur në më shumë se një dritare shfletuesi në këtë kompjuter.", "pad.modals.userdup.advice": "Rilidhuni që të përdoret kjo dritare.", "pad.modals.unauth": "I paautorizuar", - "pad.modals.unauth.explanation": "Ndërkohë që shihnit këtë dritare, lejet tuaja kanë ndryshuar. Provoni të rilidheni.", + "pad.modals.unauth.explanation": "Ndërkohë që sheh këtë dritare, lejet e tua kanë ndryshuar. Provo të rilidhesh.", "pad.modals.looping.explanation": "Ka probleme komunikimi me shërbyesin e njëkohësimit.", "pad.modals.looping.cause": "Ndoshta jeni lidhur përmes një firewall-i ose ndërmjetësi të papërputhshëm.", "pad.modals.initsocketfail": "Nuk kapet dot shërbyesi.", @@ -89,6 +91,7 @@ "timeslider.exportCurrent": "Eksportojeni versionin e tanishëm si:", "timeslider.version": "Versioni {{version}}", "timeslider.saved": "Ruajtur më {{month}} {{day}}, {{year}}", + "timeslider.playPause": "Luaj përmbajtjet e Pad / Pauzo", "timeslider.dateformat": "{{month}}/{{day}}/{{year}} {{hours}}:{{minutes}}:{{seconds}}", "timeslider.month.january": "Janar", "timeslider.month.february": "Shkurt", diff --git a/src/locales/sr-ec.json b/src/locales/sr-ec.json index 9fd20b18..3d69d52f 100644 --- a/src/locales/sr-ec.json +++ b/src/locales/sr-ec.json @@ -3,24 +3,43 @@ "authors": [ "Aktron", "Milicevic01", - "Милан Јелисавчић" + "Милан Јелисавчић", + "Srdjan m" ] }, "index.newPad": "Нови Пад", + "index.createOpenPad": "или направите/отворите пад следећег назива:", "pad.toolbar.bold.title": "Подебљано (Ctrl-B)", "pad.toolbar.italic.title": "Искошено (Ctrl-I)", "pad.toolbar.underline.title": "Подвучено (Ctrl-U)", - "pad.toolbar.strikethrough.title": "Прецртано", - "pad.toolbar.ol.title": "Уређен списак", - "pad.toolbar.ul.title": "Неуређен списак", + "pad.toolbar.strikethrough.title": "Прецртано (Ctrl+5)", + "pad.toolbar.ol.title": "Уређен списак (Ctrl+Shift+N)", + "pad.toolbar.ul.title": "Неуређен списак (Ctrl+Shift+L)", "pad.toolbar.indent.title": "Увлачење (TAB)", + "pad.toolbar.unindent.title": "Извлачење (Shift+TAB)", "pad.toolbar.undo.title": "Опозови (Ctrl+Z)", + "pad.toolbar.redo.title": "Опозови (Ctrl+Z)", + "pad.toolbar.clearAuthorship.title": "Очисти ауторске боје (Ctrl+Shift+C)", + "pad.toolbar.import_export.title": "Увези/извези из/на друге датотечне формате", + "pad.toolbar.timeslider.title": "Временска линија", + "pad.toolbar.savedRevision.title": "Сачувај верзију", "pad.toolbar.settings.title": "Подешавања", + "pad.toolbar.embed.title": "Сачувај и угради овај пад", + "pad.toolbar.showusers.title": "Прикажи кориснике на овом паду", "pad.colorpicker.save": "Сачувај", "pad.colorpicker.cancel": "Откажи", "pad.loading": "Учитавање...", + "pad.noCookie": "Колачић није пронађен. Молимо да укључите колачиће у вашем прегледавачу!", + "pad.passwordRequired": "Требате лозинку како бисте приступили овом паду", + "pad.permissionDenied": "Немате дозволу да приступите овом паду", "pad.wrongPassword": "Ваша лозинка није исправна", + "pad.settings.padSettings": "Подешавања пада", "pad.settings.myView": "Мој приказ", + "pad.settings.stickychat": "Ћаскање увек на екрану", + "pad.settings.chatandusers": "Прикажи ћаскање и кориснике", + "pad.settings.colorcheck": "Ауторске боје", + "pad.settings.linenocheck": "Бројеви редова", + "pad.settings.rtlcheck": "Читај садржај с десна на лево?", "pad.settings.fontType": "Врста фонта:", "pad.settings.fontType.normal": "Нормално", "pad.settings.fontType.monospaced": "Monospace", @@ -29,18 +48,57 @@ "pad.importExport.import_export": "Увоз/извоз", "pad.importExport.import": "Отпремите било коју текстуалну датотеку или документ", "pad.importExport.importSuccessful": "Успело!", + "pad.importExport.export": "Извези тренутни пад као:", + "pad.importExport.exportetherpad": "Etherpad", "pad.importExport.exporthtml": "HTML", "pad.importExport.exportplain": "чист текст", + "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", + "pad.importExport.exportopen": "ODF (Open Document Format)", + "pad.importExport.abiword.innerHTML": "Једино можете увести са једноставног текстуалног формата или HTML формата. За компликованије функције о увозу, молимо да <a href=\"https://github.com/ether/etherpad-lite/wiki/How-to-enable-importing-and-exporting-different-file-formats-in-Ubuntu-or-OpenSuse-or-SLES-with-AbiWord\">инсталирате AbiWord</a>.", "pad.modals.connected": "Повезано.", + "pad.modals.reconnecting": "Поново се повезујем на ваш пад..", + "pad.modals.forcereconnect": "Присилно се поново повежи", + "pad.modals.userdup": "Отворено у другом прозору", + "pad.modals.userdup.explanation": "Изгледа да је овај пад отворен у два или више прозора на овом рачунару.", + "pad.modals.userdup.advice": "Поново се повежите на овој прозор.", + "pad.modals.unauth": "Нисте овлашћени", + "pad.modals.unauth.explanation": "Ваша допуштења се се променила док сте прегледавали страницу. Покушајте се поново повезати.", + "pad.modals.looping.explanation": "Постоје комуникацијски проблеми са синхронизационим сервером.", + "pad.modals.looping.cause": "Можда сте се повезали преко неподржаног заштитног зида или проксија.", + "pad.modals.initsocketfail": "Сервер је недоступан.", + "pad.modals.initsocketfail.explanation": "Не могу се повезати на синхронизациони сервер.", + "pad.modals.initsocketfail.cause": "Највероватније је дошло до проблем са вашим прегледачем или вашом интернетском везом.", "pad.modals.slowcommit.explanation": "Сервер не одговара.", + "pad.modals.slowcommit.cause": "Највероватније је дошло до проблема са мрежном повезаношћу.", + "pad.modals.badChangeset.explanation": "Синхронизациони сервер је уређивање које сте начили означио као неисправно.", + "pad.modals.badChangeset.cause": "Могуће да је дошло до погрешне конфигурације сервера или неког другог неочекиваног догађаја. Молимо вас да контактирате сервисног администратора ако мислите да је ово грешка. Покушајте се поново повезати како бисте наставили с уређивањем.", + "pad.modals.corruptPad.explanation": "Пад којем покушавате приступити је оштећен.", + "pad.modals.corruptPad.cause": "Могуће да је дошло до погрешне конфигурације сервера или неког другог неочекиваног догађаја. Молимо вас да контактирате сервисног администратора.", "pad.modals.deleted": "Обрисано.", + "pad.modals.deleted.explanation": "Овај пад је уклоњен.", + "pad.modals.disconnected": "Веза је прекинута.", + "pad.modals.disconnected.explanation": "Изгубљена је веза са сервером", + "pad.modals.disconnected.cause": "Сервер није доступан. Обавестите сервисног администратора ако се ово настави дешавати.", "pad.share": "Дели овај пад", "pad.share.readonly": "Само за читање", "pad.share.link": "Веза", + "pad.share.emebdcode": "Угради везу", "pad.chat": "Ћаскање", "pad.chat.title": "Отворите ћаскање за овај пад.", "pad.chat.loadmessages": "Учитајте више порука.", + "timeslider.pageTitle": "{{appTitle}} временска линија", + "timeslider.toolbar.returnbutton": "Врати се на пад", + "timeslider.toolbar.authors": "Аутори:", + "timeslider.toolbar.authorsList": "Нема аутора", + "timeslider.toolbar.exportlink.title": "Извези", + "timeslider.exportCurrent": "Извези тренутну верзију као:", + "timeslider.version": "Верзија {{version}}", + "timeslider.saved": "Сачувано на {{day}}. {{month}}. {{year}}", + "timeslider.playPause": "Пусти/паузирај садржај пада", + "timeslider.backRevision": "Иди на претходну верзију овог пада", + "timeslider.forwardRevision": "Иди на следећу верзију овог пада", + "timeslider.dateformat": "{{month}}/{{day}}/{{year}} {{hours}}:{{minutes}}:{{seconds}}", "timeslider.month.january": "јануар", "timeslider.month.february": "фебруар", "timeslider.month.march": "март", @@ -53,7 +111,22 @@ "timeslider.month.october": "октобар", "timeslider.month.november": "новембар", "timeslider.month.december": "децембар", + "timeslider.unnamedauthors": "{{num}} неименован(и) {[plural(num) one: аутор, other: аутори ]}", + "pad.savedrevs.marked": "Ова верзија је сада означена као сачувана", + "pad.savedrevs.timeslider": "Можете видети сачуване измене користећи се временском линијом", + "pad.userlist.entername": "Упишите своје име", + "pad.userlist.unnamed": "нема имена", + "pad.userlist.guest": "Гост", + "pad.userlist.deny": "Одбиј", "pad.userlist.approve": "одобрено", + "pad.editbar.clearcolors": "Очисти ауторске боје за цели документ?", "pad.impexp.importbutton": "Увези одмах", - "pad.impexp.importing": "Увожење..." + "pad.impexp.importing": "Увожење...", + "pad.impexp.confirmimport": "Увоз датотеке ће преписати тренутни текст пада. Да ли сте сигурни да желите наставити?", + "pad.impexp.convertFailed": "Не можемо увести ову датотеку. Молимо да користите други формат документа или да документ копирате ручно", + "pad.impexp.padHasData": "Не можемо да увеземо ову датотеку зато што је већ било промена на овом паду, молимо да увезете нови пад", + "pad.impexp.uploadFailed": "Отпремање није успело, молимо да покушате поново", + "pad.impexp.importfailed": "Увоз неуспешан", + "pad.impexp.copypaste": "Молимо да ручно копирате", + "pad.impexp.exportdisabled": "Извоз у формату {{type}} није дозвољен. Контактирајте системског администратора за детаље." } diff --git a/src/locales/tr.json b/src/locales/tr.json index 2c948ef6..bed5c6ec 100644 --- a/src/locales/tr.json +++ b/src/locales/tr.json @@ -5,7 +5,8 @@ "Erdemaslancan", "Joseph", "Meelo", - "Trockya" + "Trockya", + "McAang" ] }, "index.newPad": "Yeni Bloknot", diff --git a/src/locales/uk.json b/src/locales/uk.json index 9dcde98e..d5384a55 100644 --- a/src/locales/uk.json +++ b/src/locales/uk.json @@ -6,7 +6,8 @@ "Olvin", "Steve.rusyn", "SteveR", - "Lxlalexlxl" + "Lxlalexlxl", + "Григорій Пугач" ] }, "index.newPad": "Створити", @@ -97,6 +98,9 @@ "timeslider.exportCurrent": "Експортувати поточну версію як:", "timeslider.version": "Версія {{version}}", "timeslider.saved": "Збережено {{month}} {{day}}, {{year}}", + "timeslider.playPause": "Відтворення / Пауза Панель Зміст", + "timeslider.backRevision": "Переглянути попередню ревізію цієї панелі", + "timeslider.forwardRevision": "Переглянути наступну ревізію цієї панелі", "timeslider.dateformat": "{{month}}/{{day}}/{{year}} {{hours}}:{{minutes}}:{{seconds}}", "timeslider.month.january": "Січень", "timeslider.month.february": "Лютий", @@ -112,6 +116,7 @@ "timeslider.month.december": "Грудень", "timeslider.unnamedauthors": "{{num}} {[plural(num) one: безіменний автор, few: безіменні автори, many: безіменних авторів, other: безіменних авторів]}", "pad.savedrevs.marked": "Цю версію помічено збереженою версією", + "pad.savedrevs.timeslider": "Ви можете побачити збережені ревізії, відвідавши \"Слайдер Змін Ревізій\"", "pad.userlist.entername": "Введіть Ваше ім'я", "pad.userlist.unnamed": "безіменний", "pad.userlist.guest": "Гість", @@ -122,6 +127,7 @@ "pad.impexp.importing": "Імпорт...", "pad.impexp.confirmimport": "Імпортування файлу перезапише поточний текст документу. Ви дійсно хочете продовжити?", "pad.impexp.convertFailed": "Ми не можемо імпортувати цей файл. Будь ласка, використайте інший формат документу, або прямо скопіюйте та вставте", + "pad.impexp.padHasData": "Ми були не в стані імпортувати цей файл, тому що ця панель, вже відредактована, будь ласка, імпортуйте на нову панель", "pad.impexp.uploadFailed": "Завантаження не вдалось, будь ласка, спробуйте знову", "pad.impexp.importfailed": "Помилка при імпортуванні", "pad.impexp.copypaste": "Будь ласка, скопіюйте та вставте", diff --git a/src/locales/zh-hans.json b/src/locales/zh-hans.json index eaa03520..15f11591 100644 --- a/src/locales/zh-hans.json +++ b/src/locales/zh-hans.json @@ -82,9 +82,9 @@ "pad.modals.badChangeset.cause": "这可能是因为服务器配置的错误或者其他未预料到的行为。如果您认为这是错误,请联系服务管理员。要继续编辑,请尝试重新连接。", "pad.modals.corruptPad.explanation": "您试图连接的记事本已损坏。", "pad.modals.corruptPad.cause": "这可能是因为服务器配置的错误或者其他未预料到的行为。请联系服务管理员。", - "pad.modals.deleted": "已刪除。", + "pad.modals.deleted": "已删除。", "pad.modals.deleted.explanation": "此记事本已被移除。", - "pad.modals.disconnected": "你已断开连接。", + "pad.modals.disconnected": "您已断开连接。", "pad.modals.disconnected.explanation": "到服务器的连接已丢失", "pad.modals.disconnected.cause": "服务器可能无法使用。若此情况持续发生,请通知服务器管理员。", "pad.share": "分享此记事本", diff --git a/src/locales/zh-hant.json b/src/locales/zh-hant.json index 48dd577c..8bdf29dc 100644 --- a/src/locales/zh-hant.json +++ b/src/locales/zh-hant.json @@ -28,7 +28,7 @@ "pad.toolbar.savedRevision.title": "儲存修訂", "pad.toolbar.settings.title": "設定", "pad.toolbar.embed.title": "分享和嵌入此pad", - "pad.toolbar.showusers.title": "顯示此pad的用戶", + "pad.toolbar.showusers.title": "顯示此 pad 的使用者", "pad.colorpicker.save": "儲存", "pad.colorpicker.cancel": "取消", "pad.loading": "載入中...", @@ -39,7 +39,7 @@ "pad.settings.padSettings": "Pad設定", "pad.settings.myView": "我的視窗", "pad.settings.stickychat": "永遠在屏幕上顯示聊天", - "pad.settings.chatandusers": "顯示聊天及用戶", + "pad.settings.chatandusers": "顯示聊天與使用者", "pad.settings.colorcheck": "協作者顏色", "pad.settings.linenocheck": "行號", "pad.settings.rtlcheck": "從右至左讀取內容?", diff --git a/src/node/db/API.js b/src/node/db/API.js index 97d5162d..237bcb0a 100644 --- a/src/node/db/API.js +++ b/src/node/db/API.js @@ -308,6 +308,38 @@ exports.setText = function(padID, text, callback) } /** +appendText(padID, text) appends text to a pad + +Example returns: + +{code: 0, message:"ok", data: null} +{code: 1, message:"padID does not exist", data: null} +{code: 1, message:"text too long", data: null} +*/ +exports.appendText = function(padID, text, callback) +{ + //text is required + if(typeof text != "string") + { + callback(new customError("text is no string","apierror")); + return; + } + + //get the pad + getPadSafe(padID, true, function(err, pad) + { + if(ERR(err, callback)) return; + + pad.appendText(text); + + //update the clients on the pad + padMessageHandler.updatePadClients(pad, callback); + }); +}; + + + +/** getHTML(padID, [rev]) returns the html of a pad Example returns: @@ -473,9 +505,9 @@ exports.getChatHistory = function(padID, start, end, callback) end = pad.chatHead; } - if(start >= chatHead && chatHead > 0) + if(start > chatHead) { - callback(new customError("start is higher or equal to the current chatHead","apierror")); + callback(new customError("start is higher than the current chatHead","apierror")); return; } if(end > chatHead) @@ -499,7 +531,7 @@ appendChatMessage(padID, text, authorID, time), creates a chat message for the p Example returns: -{code: 0, message:"ok", data: null +{code: 0, message:"ok", data: null} {code: 1, message:"padID does not exist", data: null} */ exports.appendChatMessage = function(padID, text, authorID, time, callback) @@ -510,15 +542,19 @@ exports.appendChatMessage = function(padID, text, authorID, time, callback) callback(new customError("text is no string","apierror")); return; } - - //get the pad - getPadSafe(padID, true, function(err, pad) + + // if time is not an integer value + if(time === undefined || !is_int(time)) { - if(ERR(err, callback)) return; - - pad.appendChatMessage(text, authorID, parseInt(time)); - callback(); - }); + // set time to current timestamp + time = new Date().getTime(); + } + + var padMessage = require("ep_etherpad-lite/node/handler/PadMessageHandler.js"); + // save chat message to database and send message to all connected clients + padMessage.sendChatMessageToPadClients(parseInt(time), authorID, text, padID); + + callback(); } /*****************/ diff --git a/src/node/db/Pad.js b/src/node/db/Pad.js index eb6a3ed1..83e15e6d 100644 --- a/src/node/db/Pad.js +++ b/src/node/db/Pad.js @@ -303,6 +303,19 @@ Pad.prototype.setText = function setText(newText) { this.appendRevision(changeset); }; +Pad.prototype.appendText = function appendText(newText) { + //clean the new text + newText = exports.cleanText(newText); + + var oldText = this.text(); + + //create the changeset + var changeset = Changeset.makeSplice(oldText, oldText.length, 0, newText); + + //append the changeset + this.appendRevision(changeset); +}; + Pad.prototype.appendChatMessage = function appendChatMessage(text, userId, time) { this.chatHead++; //save the chat entry in the database diff --git a/src/node/handler/APIHandler.js b/src/node/handler/APIHandler.js index b4d24201..179c2b40 100644 --- a/src/node/handler/APIHandler.js +++ b/src/node/handler/APIHandler.js @@ -444,10 +444,61 @@ var version = , "getChatHead" : ["padID"] , "restoreRevision" : ["padID", "rev"] } +, "1.2.13": + { "createGroup" : [] + , "createGroupIfNotExistsFor" : ["groupMapper"] + , "deleteGroup" : ["groupID"] + , "listPads" : ["groupID"] + , "listAllPads" : [] + , "createDiffHTML" : ["padID", "startRev", "endRev"] + , "createPad" : ["padID", "text"] + , "createGroupPad" : ["groupID", "padName", "text"] + , "createAuthor" : ["name"] + , "createAuthorIfNotExistsFor": ["authorMapper" , "name"] + , "listPadsOfAuthor" : ["authorID"] + , "createSession" : ["groupID", "authorID", "validUntil"] + , "deleteSession" : ["sessionID"] + , "getSessionInfo" : ["sessionID"] + , "listSessionsOfGroup" : ["groupID"] + , "listSessionsOfAuthor" : ["authorID"] + , "getText" : ["padID", "rev"] + , "setText" : ["padID", "text"] + , "getHTML" : ["padID", "rev"] + , "setHTML" : ["padID", "html"] + , "getAttributePool" : ["padID"] + , "getRevisionsCount" : ["padID"] + , "getSavedRevisionsCount" : ["padID"] + , "listSavedRevisions" : ["padID"] + , "saveRevision" : ["padID", "rev"] + , "getRevisionChangeset" : ["padID", "rev"] + , "getLastEdited" : ["padID"] + , "deletePad" : ["padID"] + , "copyPad" : ["sourceID", "destinationID", "force"] + , "movePad" : ["sourceID", "destinationID", "force"] + , "getReadOnlyID" : ["padID"] + , "getPadID" : ["roID"] + , "setPublicStatus" : ["padID", "publicStatus"] + , "getPublicStatus" : ["padID"] + , "setPassword" : ["padID", "password"] + , "isPasswordProtected" : ["padID"] + , "listAuthorsOfPad" : ["padID"] + , "padUsersCount" : ["padID"] + , "getAuthorName" : ["authorID"] + , "padUsers" : ["padID"] + , "sendClientsMessage" : ["padID", "msg"] + , "listAllGroups" : [] + , "checkToken" : [] + , "appendChatMessage" : ["padID", "text", "authorID", "time"] + , "getChatHistory" : ["padID"] + , "getChatHistory" : ["padID", "start", "end"] + , "getChatHead" : ["padID"] + , "restoreRevision" : ["padID", "rev"] + , "appendText" : ["padID", "text"] + } }; // set the latest available API version here -exports.latestApiVersion = '1.2.12'; +exports.latestApiVersion = '1.2.13'; // exports the versions so it can be used by the new Swagger endpoint exports.version = version; diff --git a/src/node/handler/ExportHandler.js b/src/node/handler/ExportHandler.js index f861c82e..0a808977 100644 --- a/src/node/handler/ExportHandler.js +++ b/src/node/handler/ExportHandler.js @@ -30,9 +30,15 @@ var os = require('os'); var hooks = require("ep_etherpad-lite/static/js/pluginfw/hooks"); var TidyHtml = require('../utils/TidyHtml'); +var convertor = null; + //load abiword only if its enabled if(settings.abiword != null) - var abiword = require("../utils/Abiword"); + convertor = require("../utils/Abiword"); + +// Use LibreOffice if an executable has been defined in the settings +if(settings.soffice != null) + convertor = require("../utils/LibreOffice"); var tempDirectory = "/tmp"; @@ -70,71 +76,11 @@ exports.doExport = function(req, res, padId, type) } else if(type == "txt") { - var txt; - var randNum; - var srcFile, destFile; - - async.series([ - //render the txt document - function(callback) - { - exporttxt.getPadTXTDocument(padId, req.params.rev, false, function(err, _txt) - { - if(ERR(err, callback)) return; - txt = _txt; - callback(); - }); - }, - //decide what to do with the txt export - function(callback) - { - //if this is a txt export, we can send this from here directly - res.send(txt); - callback("stop"); - }, - //send the convert job to abiword - function(callback) - { - //ensure html can be collected by the garbage collector - txt = null; - - destFile = tempDirectory + "/etherpad_export_" + randNum + "." + type; - abiword.convertFile(srcFile, destFile, type, callback); - }, - //send the file - function(callback) - { - res.sendFile(destFile, null, callback); - }, - //clean up temporary files - function(callback) - { - async.parallel([ - function(callback) - { - fs.unlink(srcFile, callback); - }, - function(callback) - { - //100ms delay to accomidate for slow windows fs - if(os.type().indexOf("Windows") > -1) - { - setTimeout(function() - { - fs.unlink(destFile, callback); - }, 100); - } - else - { - fs.unlink(destFile, callback); - } - } - ], callback); - } - ], function(err) + exporttxt.getPadTXTDocument(padId, req.params.rev, false, function(err, txt) { - if(err && err != "stop") ERR(err); - }) + if(ERR(err)) return; + res.send(txt); + }); } else { @@ -183,11 +129,11 @@ exports.doExport = function(req, res, padId, type) TidyHtml.tidy(srcFile, callback); }, - //send the convert job to abiword + //send the convert job to the convertor (abiword, libreoffice, ..) function(callback) { destFile = tempDirectory + "/etherpad_export_" + randNum + "." + type; - abiword.convertFile(srcFile, destFile, type, callback); + convertor.convertFile(srcFile, destFile, type, callback); }, //send the file function(callback) diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index 248dc128..9481889f 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -375,6 +375,17 @@ function handleChatMessage(client, message) var text = message.data.text; var padId = sessioninfos[client.id].padId; + exports.sendChatMessageToPadClients(time, userId, text, padId); +} + +/** + * Sends a chat message to all clients of this pad + * @param time the timestamp of the chat message + * @param userId the author id of the chat message + * @param text the text of the chat message + * @param padId the padId to send the chat message to + */ +exports.sendChatMessageToPadClients = function (time, userId, text, padId) { var pad; var userName; @@ -619,8 +630,8 @@ function handleUserChanges(data, cb) messageLogger.warn("Dropped message, USER_CHANGES Message has no changeset!"); return cb(); } - //TODO: this might happen with other messages too => find one place to copy the session - //and always use the copy. atm a message will be ignored if the session is gone even + //TODO: this might happen with other messages too => find one place to copy the session + //and always use the copy. atm a message will be ignored if the session is gone even //if the session was valid when the message arrived in the first place if(!sessioninfos[client.id]) { @@ -949,7 +960,7 @@ function handleSwitchToPad(client, message) roomClients[i].leave(padId); } } - + // start up the new pad createSessionInfo(client, message); handleClientReady(client, message); @@ -1009,6 +1020,8 @@ function handleClientReady(client, message) var currentTime; var padIds; + hooks.callAll("clientReady", message); + async.series([ //Get ro/rw id:s function (callback) @@ -1218,6 +1231,7 @@ function handleClientReady(client, message) "plugins": plugins.plugins, "parts": plugins.parts, }, + "indentationOnNewLine": settings.indentationOnNewLine, "initialChangesets": [] // FIXME: REMOVE THIS SHIT } @@ -1354,6 +1368,12 @@ function handleChangesetRequest(client, message) messageLogger.warn("Dropped message, changeset request has no granularity!"); return; } + //https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger#Polyfill + if(Math.floor(message.data.granularity) !== message.data.granularity) + { + messageLogger.warn("Dropped message, changeset request granularity is not an integer!"); + return; + } if(message.data.start == null) { messageLogger.warn("Dropped message, changeset request has no start!"); diff --git a/src/node/hooks/express/padurlsanitize.js b/src/node/hooks/express/padurlsanitize.js index 94cbe36a..a9972220 100644 --- a/src/node/hooks/express/padurlsanitize.js +++ b/src/node/hooks/express/padurlsanitize.js @@ -16,6 +16,7 @@ exports.expressCreateServer = function (hook_name, args, cb) { if(sanitizedPadId != padId) { var real_url = sanitizedPadId; + real_url = encodeURIComponent(real_url); var query = url.parse(req.url).query; if ( query ) real_url += '?' + query; res.header('Location', real_url); diff --git a/src/node/utils/ExportHtml.js b/src/node/utils/ExportHtml.js index 9e1ba124..ffc7bc58 100644 --- a/src/node/utils/ExportHtml.js +++ b/src/node/utils/ExportHtml.js @@ -19,6 +19,7 @@ var async = require("async"); var Changeset = require("ep_etherpad-lite/static/js/Changeset"); var padManager = require("../db/PadManager"); var ERR = require("async-stacktrace"); +var _ = require('underscore'); var Security = require('ep_etherpad-lite/static/js/security'); var hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks'); var _analyzeLine = require('./ExportHelper')._analyzeLine; @@ -78,8 +79,15 @@ function getHTMLFromAtext(pad, atext, authorColors) var props = ['heading1', 'heading2', 'bold', 'italic', 'underline', 'strikethrough']; hooks.aCallAll("exportHtmlAdditionalTags", pad, function(err, newProps){ + // newProps can be simply a string (which means it is stored as attribute in the form of ['tag', 'true']) + // or it can be a pair of values in an Array (for the case when it is stored as ['tag', 'value']). + // The later scenario will generate HTML with tags like <span data-tag="value"> newProps.forEach(function (propName, i){ - tags.push(propName); + if (_.isArray(propName)) { + tags.push('span data-' + propName[0] + '="' + propName[1] + '"'); + } else { + tags.push(propName); + } props.push(propName); }); }); @@ -115,8 +123,8 @@ function getHTMLFromAtext(pad, atext, authorColors) var newLength = props.push(propName); anumMap[a] = newLength -1; - css+=".removed {text-decoration: line-through; " + - "-ms-filter:'progid:DXImageTransform.Microsoft.Alpha(Opacity=80)'; "+ + css+=".removed {text-decoration: line-through; " + + "-ms-filter:'progid:DXImageTransform.Microsoft.Alpha(Opacity=80)'; "+ "filter: alpha(opacity=80); "+ "opacity: 0.8; "+ "}\n"; @@ -130,7 +138,12 @@ function getHTMLFromAtext(pad, atext, authorColors) // this pad, and if yes puts its attrib id->props value into anumMap props.forEach(function (propName, i) { - var propTrueNum = apool.putAttrib([propName, true], true); + var attrib = [propName, true]; + if (_.isArray(propName)) { + // propName can be in the form of ['color', 'red'] + attrib = propName; + } + var propTrueNum = apool.putAttrib(attrib, true); if (propTrueNum >= 0) { anumMap[propTrueNum] = i; @@ -154,6 +167,11 @@ function getHTMLFromAtext(pad, atext, authorColors) var property = props[i]; + // we are not insterested on properties in the form of ['color', 'red'] + if (_.isArray(property)) { + return false; + } + if(property.substr(0,6) === "author"){ return stripDotFromAuthorID(property); } @@ -165,6 +183,11 @@ function getHTMLFromAtext(pad, atext, authorColors) return false; } + function isSpanWithData(i){ + var property = props[i]; + return _.isArray(property); + } + function emitOpenTag(i) { openTags.unshift(i); @@ -186,8 +209,9 @@ function getHTMLFromAtext(pad, atext, authorColors) { openTags.shift(); var spanClass = getSpanClassFor(i); + var spanWithData = isSpanWithData(i); - if(spanClass){ + if(spanClass || spanWithData){ assem.append('</span>'); } else { assem.append('</'); @@ -263,7 +287,7 @@ function getHTMLFromAtext(pad, atext, authorColors) var s = taker.take(chars); - //removes the characters with the code 12. Don't know where they come + //removes the characters with the code 12. Don't know where they come //from but they break the abiword parser and are completly useless s = s.replace(String.fromCharCode(12), ""); @@ -377,7 +401,7 @@ function getHTMLFromAtext(pad, atext, authorColors) pieces.push('<br><br>'); } }*/ - else//means we are getting closer to the lowest level of indentation or are at the same level + else//means we are getting closer to the lowest level of indentation or are at the same level { var toClose = lists.length > 0 ? listLevels[listLevels.length - 2] - line.listLevel : 0 if( toClose > 0){ @@ -431,7 +455,7 @@ function getHTMLFromAtext(pad, atext, authorColors) } } } - + for (var k = lists.length - 1; k >= 0; k--) { if(lists[k][1] == "number") @@ -460,14 +484,17 @@ exports.getPadHTMLDocument = function (padId, revNum, noDocType, callback) stylesForExportCSS += css; }); // Core inclusion of head etc. - var head = - (noDocType ? '' : '<!doctype html>\n') + - '<html lang="en">\n' + (noDocType ? '' : '<head>\n' + + var head = + (noDocType ? '' : '<!doctype html>\n') + + '<html lang="en">\n' + (noDocType ? '' : '<head>\n' + '<title>' + Security.escapeHTML(padId) + '</title>\n' + - '<meta charset="utf-8">\n' + - '<style> * { font-family: arial, sans-serif;\n' + - 'font-size: 13px;\n' + - 'line-height: 17px; }' + + '<meta name="generator" content="Etherpad">\n' + + '<meta name="author" content="Etherpad">\n' + + '<meta name="changedby" content="Etherpad">\n' + + '<meta charset="utf-8">\n' + + '<style> * { font-family: arial, sans-serif;\n' + + 'font-size: 13px;\n' + + 'line-height: 17px; }' + 'ul.indent { list-style-type: none; }' + 'ol { list-style-type: none; padding-left:0;}' + @@ -553,8 +580,8 @@ exports.getPadHTMLDocument = function (padId, revNum, noDocType, callback) 'ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol{ text-indent: 140px; }' + 'ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol{ text-indent: 150px; }' + - stylesForExportCSS + - '</style>\n' + '</head>\n') + + stylesForExportCSS + + '</style>\n' + '</head>\n') + '<body>'; var foot = '</body>\n</html>\n'; diff --git a/src/node/utils/LibreOffice.js b/src/node/utils/LibreOffice.js new file mode 100644 index 00000000..41577245 --- /dev/null +++ b/src/node/utils/LibreOffice.js @@ -0,0 +1,93 @@ +/** + * Controls the communication with LibreOffice + */ + +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS-IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var async = require("async"); +var fs = require("fs"); +var os = require("os"); +var path = require("path"); +var settings = require("./Settings"); +var spawn = require("child_process").spawn; + +// Conversion tasks will be queued up, so we don't overload the system +var queue = async.queue(doConvertTask, 1); + +/** + * Convert a file from one type to another + * + * @param {String} srcFile The path on disk to convert + * @param {String} destFile The path on disk where the converted file should be stored + * @param {String} type The type to convert into + * @param {Function} callback Standard callback function + */ +exports.convertFile = function(srcFile, destFile, type, callback) { + queue.push({"srcFile": srcFile, "destFile": destFile, "type": type, "callback": callback}); +}; + +function doConvertTask(task, callback) { + var tmpDir = os.tmpdir(); + + async.series([ + // Generate a PDF file with LibreOffice + function(callback) { + var soffice = spawn(settings.soffice, [ + '--headless', + '--invisible', + '--nologo', + '--nolockcheck', + '--convert-to', task.type, + task.srcFile, + '--outdir', tmpDir + ]); + + var stdoutBuffer = ''; + + // Delegate the processing of stdout to another function + soffice.stdout.on('data', function(data) { + stdoutBuffer += data.toString(); + }); + + // Append error messages to the buffer + soffice.stderr.on('data', function(data) { + stdoutBuffer += data.toString(); + }); + + // Throw an exception if libreoffice failed + soffice.on('exit', function(code) { + if (code != 0) { + return callback("LibreOffice died with exit code " + code + " and message: " + stdoutBuffer); + } + + callback(); + }) + }, + + // Move the PDF file to the correct place + function(callback) { + var filename = path.basename(task.srcFile); + var pdfFilename = filename.substr(0, filename.lastIndexOf('.')) + '.' + task.type; + var pdfPath = path.join(tmpDir, pdfFilename); + fs.rename(pdfPath, task.destFile, callback); + } + ], function(err) { + // Invoke the callback for the local queue + callback(); + + // Invoke the callback for the task + task.callback(err); + }); +} diff --git a/src/node/utils/Settings.js b/src/node/utils/Settings.js index 2cc6a926..f76cebdc 100644 --- a/src/node/utils/Settings.js +++ b/src/node/utils/Settings.js @@ -83,7 +83,7 @@ exports.dbSettings = { "filename" : path.join(exports.root, "dirty.db") }; /** * The default Text of a new pad */ -exports.defaultPadText = "Welcome to Etherpad!\n\nThis 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!\n\nEtherpad on Github: http:\/\/j.mp/ep-lite\n"; +exports.defaultPadText = "Welcome to Etherpad!\n\nThis 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!\n\nEtherpad on Github: https:\/\/github.com\/ether\/etherpad-lite\n"; /** * The default Pad Settings for a user (Can be overridden by changing the setting @@ -153,6 +153,11 @@ exports.minify = true; exports.abiword = null; /** + * The path of the libreoffice executable + */ +exports.soffice = null; + +/** * The path of the tidy executable */ exports.tidyHtml = null; @@ -177,6 +182,11 @@ exports.disableIPlogging = false; */ exports.loadTest = false; +/** + * Enable indentation on new lines + */ +exports.indentationOnNewLine = true; + /* * log4js appender configuration */ @@ -218,8 +228,14 @@ exports.getGitCommit = function() { try { var rootPath = path.resolve(npm.dir, '..'); - var ref = fs.readFileSync(rootPath + "/.git/HEAD", "utf-8"); - var refPath = rootPath + "/.git/" + ref.substring(5, ref.indexOf("\n")); + if (fs.lstatSync(rootPath + '/.git').isFile()) { + rootPath = fs.readFileSync(rootPath + '/.git', "utf8"); + rootPath = rootPath.split(' ').pop().trim(); + } else { + rootPath += '/.git'; + } + var ref = fs.readFileSync(rootPath + "/HEAD", "utf-8"); + var refPath = rootPath + "/" + ref.substring(5, ref.indexOf("\n")); version = fs.readFileSync(refPath, "utf-8"); version = version.substring(0, 7); } diff --git a/src/package.json b/src/package.json index bfd3f260..4e905429 100644 --- a/src/package.json +++ b/src/package.json @@ -55,5 +55,5 @@ "repository" : { "type" : "git", "url" : "http://github.com/ether/etherpad-lite.git" }, - "version" : "1.5.6" + "version" : "1.5.7" } diff --git a/src/static/css/pad.css b/src/static/css/pad.css index d21e84dd..5764c5e4 100644 --- a/src/static/css/pad.css +++ b/src/static/css/pad.css @@ -216,6 +216,9 @@ li[data-key=showusers] > a #online_count { right: 0px; bottom: 0px; z-index: 1; + + /* Required to be able to scroll on iOS: */ + -webkit-overflow-scrolling: touch; } #editorcontainer iframe { height: 100%; diff --git a/src/static/css/timeslider.css b/src/static/css/timeslider.css index 9f4e4683..2756a006 100644 --- a/src/static/css/timeslider.css +++ b/src/static/css/timeslider.css @@ -232,7 +232,7 @@ stepper:active{ .timeslider-bar #editbar { border-bottom: none; float: right; - width: 180px; + width: auto; } .timeslider-bar h1 { margin: 5px diff --git a/src/static/js/AttributeManager.js b/src/static/js/AttributeManager.js index 3f464908..d4d20a1f 100644 --- a/src/static/js/AttributeManager.js +++ b/src/static/js/AttributeManager.js @@ -4,23 +4,23 @@ var _ = require('./underscore'); var lineMarkerAttribute = 'lmkr'; -// If one of these attributes are set to the first character of a +// If one of these attributes are set to the first character of a // line it is considered as a line attribute marker i.e. attributes -// set on this marker are applied to the whole line. +// set on this marker are applied to the whole line. // The list attribute is only maintained for compatibility reasons var lineAttributes = [lineMarkerAttribute,'list']; /* - The Attribute manager builds changesets based on a document + The Attribute manager builds changesets based on a document representation for setting and removing range or line-based attributes. - + @param rep the document representation to be used - @param applyChangesetCallback this callback will be called + @param applyChangesetCallback this callback will be called once a changeset has been built. - - - A document representation contains - - an array `alines` containing 1 attributes string for each line + + + A document representation contains + - an array `alines` containing 1 attributes string for each line - an Attribute pool `apool` - a SkipList `lines` containing the text lines of the document. */ @@ -30,7 +30,7 @@ var AttributeManager = function(rep, applyChangesetCallback) this.rep = rep; this.applyChangesetCallback = applyChangesetCallback; this.author = ''; - + // If the first char in a line has one of the following attributes // it will be considered as a line marker }; @@ -38,49 +38,106 @@ var AttributeManager = function(rep, applyChangesetCallback) AttributeManager.lineAttributes = lineAttributes; AttributeManager.prototype = _(AttributeManager.prototype).extend({ - + applyChangeset: function(changeset){ if(!this.applyChangesetCallback) return changeset; - + var cs = changeset.toString(); if (!Changeset.isIdentity(cs)) { this.applyChangesetCallback(cs); } - + return changeset; }, - + /* Sets attributes on a range @param start [row, col] tuple pointing to the start of the range @param end [row, col] tuple pointing to the end of the range - @param attribute: an array of attributes + @param attribs: an array of attributes */ setAttributesOnRange: function(start, end, attribs) { + // instead of applying the attributes to the whole range at once, we need to apply them + // line by line, to be able to disregard the "*" used as line marker. For more details, + // see https://github.com/ether/etherpad-lite/issues/2772 + var allChangesets; + for(var row = start[0]; row <= end[0]; row++) { + var rowRange = this._findRowRange(row, start, end); + var startCol = rowRange[0]; + var endCol = rowRange[1]; + + var rowChangeset = this._setAttributesOnRangeByLine(row, startCol, endCol, attribs); + + // compose changesets of all rows into a single changeset, as the range might not be continuous + // due to the presence of line markers on the rows + if (allChangesets) { + allChangesets = Changeset.compose(allChangesets.toString(), rowChangeset.toString(), this.rep.apool); + } else { + allChangesets = rowChangeset; + } + } + + return this.applyChangeset(allChangesets); + }, + + _findRowRange: function(row, start, end) + { + var startCol, endCol; + + var startLineOffset = this.rep.lines.offsetOfIndex(row); + var endLineOffset = this.rep.lines.offsetOfIndex(row+1); + var lineLength = endLineOffset - startLineOffset; + + // find column where range on this row starts + if (row === start[0]) { // are we on the first row of range? + startCol = start[1]; + } else { + startCol = this.lineHasMarker(row) ? 1 : 0; // remove "*" used as line marker + } + + // find column where range on this row ends + if (row === end[0]) { // are we on the last row of range? + endCol = end[1]; // if so, get the end of range, not end of row + } else { + endCol = lineLength - 1; // remove "\n" + } + + return [startCol, endCol]; + }, + + /* + Sets attributes on a range, by line + @param row the row where range is + @param startCol column where range starts + @param endCol column where range ends + @param attribs: an array of attributes + */ + _setAttributesOnRangeByLine: function(row, startCol, endCol, attribs) + { var builder = Changeset.builder(this.rep.lines.totalWidth()); - ChangesetUtils.buildKeepToStartOfRange(this.rep, builder, start); - ChangesetUtils.buildKeepRange(this.rep, builder, start, end, attribs, this.rep.apool); - return this.applyChangeset(builder); + ChangesetUtils.buildKeepToStartOfRange(this.rep, builder, [row, startCol]); + ChangesetUtils.buildKeepRange(this.rep, builder, [row, startCol], [row, endCol], attribs, this.rep.apool); + return builder; }, - /* + /* Returns if the line already has a line marker @param lineNum: the number of the line */ lineHasMarker: function(lineNum){ var that = this; - + return _.find(lineAttributes, function(attribute){ - return that.getAttributeOnLine(lineNum, attribute) != ''; + return that.getAttributeOnLine(lineNum, attribute) != ''; }) !== undefined; }, - + /* Gets a specified attribute on a line @param lineNum: the number of the line to set the attribute for - @param attributeKey: the name of the attribute to get, e.g. list + @param attributeKey: the name of the attribute to get, e.g. list */ getAttributeOnLine: function(lineNum, attributeName){ // get `attributeName` attribute of first char of line @@ -95,10 +152,10 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ } return ''; }, - + /* Gets all attributes on a line - @param lineNum: the number of the line to get the attribute for + @param lineNum: the number of the line to get the attribute for */ getAttributesOnLine: function(lineNum){ // get attributes of first char of line @@ -112,7 +169,7 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ { op = opIter.next() if(!op.attribs) return [] - + Changeset.eachAttribNumber(op.attribs, function(n) { attributes.push([this.rep.apool.getAttribKey(n), this.rep.apool.getAttribValue(n)]) }.bind(this)) @@ -121,33 +178,33 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ } return []; }, - + /* Gets all attributes at a position containing line number and column @param lineNumber starting with zero @param column starting with zero - returns a list of attributes in the format + returns a list of attributes in the format [ ["key","value"], ["key","value"], ... ] */ getAttributesOnPosition: function(lineNumber, column){ // get all attributes of the line var aline = this.rep.alines[lineNumber]; - + if (!aline) { return []; } // iterate through all operations of a line var opIter = Changeset.opIterator(aline); - + // we need to sum up how much characters each operations take until the wanted position var currentPointer = 0; - var attributes = []; + var attributes = []; var currentOperation; - + while (opIter.hasNext()) { currentOperation = opIter.next(); - currentPointer = currentPointer + currentOperation.chars; - + currentPointer = currentPointer + currentOperation.chars; + if (currentPointer > column) { // we got the operation of the wanted position, now collect all its attributes Changeset.eachAttribNumber(currentOperation.attribs, function (n) { @@ -156,44 +213,44 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ this.rep.apool.getAttribValue(n) ]); }.bind(this)); - + // skip the loop return attributes; } } return attributes; - + }, - + /* - Gets all attributes at caret position + Gets all attributes at caret position if the user selected a range, the start of the selection is taken - returns a list of attributes in the format + returns a list of attributes in the format [ ["key","value"], ["key","value"], ... ] */ getAttributesOnCaret: function(){ return this.getAttributesOnPosition(this.rep.selStart[0], this.rep.selStart[1]); }, - + /* Sets a specified attribute on a line @param lineNum: the number of the line to set the attribute for @param attributeKey: the name of the attribute to set, e.g. list @param attributeValue: an optional parameter to pass to the attribute (e.g. indention level) - + */ setAttributeOnLine: function(lineNum, attributeName, attributeValue){ var loc = [0,0]; var builder = Changeset.builder(this.rep.lines.totalWidth()); var hasMarker = this.lineHasMarker(lineNum); - + ChangesetUtils.buildKeepRange(this.rep, builder, loc, (loc = [lineNum, 0])); if(hasMarker){ ChangesetUtils.buildKeepRange(this.rep, builder, loc, (loc = [lineNum, 1]), [ [attributeName, attributeValue] ], this.rep.apool); - }else{ + }else{ // add a line marker builder.insert('*', [ ['author', this.author], @@ -202,10 +259,10 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ [attributeName, attributeValue] ], this.rep.apool); } - + return this.applyChangeset(builder); }, - + /** * Removes a specified attribute on a line * @param lineNum the number of the affected line @@ -243,7 +300,7 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ return this.applyChangeset(builder); }, - + /* Toggles a line attribute for the specified line number If a line attribute with the specified name exists with any value it will be removed @@ -256,7 +313,7 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ return this.getAttributeOnLine(lineNum, attributeName) ? this.removeAttributeOnLine(lineNum, attributeName) : this.setAttributeOnLine(lineNum, attributeName, attributeValue); - + } }); diff --git a/src/static/js/ace.js b/src/static/js/ace.js index c446939a..455bfaa3 100644 --- a/src/static/js/ace.js +++ b/src/static/js/ace.js @@ -265,7 +265,7 @@ plugins.ensure(function () {\n\ iframeHTML: iframeHTML }); - iframeHTML.push('</head><body id="innerdocbody" role="application" class="syntax" spellcheck="false"> </body></html>'); + iframeHTML.push('</head><body id="innerdocbody" class="innerdocbody" role="application" class="syntax" spellcheck="false"> </body></html>'); // Expose myself to global for my child frame. var thisFunctionsName = "ChildAccessibleAce2Editor"; @@ -315,7 +315,7 @@ window.onload = function () {\n\ // bizarrely, in FF2, a file with no "external" dependencies won't finish loading properly // (throbs busy while typing) - outerHTML.push('<style type="text/css" title="dynamicsyntax"></style>', '<link rel="stylesheet" type="text/css" href="data:text/css,"/>', scriptTag(outerScript), '</head><body id="outerdocbody"><div id="sidediv"><!-- --></div><div id="linemetricsdiv">x</div></body></html>'); + outerHTML.push('<style type="text/css" title="dynamicsyntax"></style>', '<link rel="stylesheet" type="text/css" href="data:text/css,"/>', scriptTag(outerScript), '</head><body id="outerdocbody" class="outerdocbody"><div id="sidediv" class="sidediv"><!-- --></div><div id="linemetricsdiv">x</div></body></html>'); var outerFrame = document.createElement("IFRAME"); outerFrame.name = "ace_outer"; diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 255f0290..d912dfb1 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -1894,7 +1894,11 @@ function Ace2Inner(){ var prevLine = rep.lines.prev(thisLine); var prevLineText = prevLine.text; var theIndent = /^ *(?:)/.exec(prevLineText)[0]; - if (/[\[\(\:\{]\s*$/.exec(prevLineText)) theIndent += THE_TAB; + var shouldIndent = parent.parent.clientVars.indentationOnNewLine; + if (shouldIndent && /[\[\(\:\{]\s*$/.exec(prevLineText)) + { + theIndent += THE_TAB; + } var cs = Changeset.builder(rep.lines.totalWidth()).keep( rep.lines.offsetOfIndex(lineNum), lineNum).insert( theIndent, [ @@ -2336,7 +2340,7 @@ function Ace2Inner(){ function getAttributeOnSelection(attributeName){ if (!(rep.selStart && rep.selEnd)) return - + var withIt = Changeset.makeAttribsString('+', [ [attributeName, 'true'] ], rep.apool); @@ -2347,14 +2351,14 @@ function Ace2Inner(){ } return rangeHasAttrib(rep.selStart, rep.selEnd) - + function rangeHasAttrib(selStart, selEnd) { // if range is collapsed -> no attribs in range if(selStart[1] == selEnd[1] && selStart[0] == selEnd[0]) return false - + if(selStart[0] != selEnd[0]) { // -> More than one line selected var hasAttrib = true - + // from selStart to the end of the first line hasAttrib = hasAttrib && rangeHasAttrib(selStart, [selStart[0], rep.lines.atIndex(selStart[0]).text.length]) @@ -2365,22 +2369,22 @@ function Ace2Inner(){ // for the last, potentially partial, line hasAttrib = hasAttrib && rangeHasAttrib([selEnd[0], 0], [selEnd[0], selEnd[1]]) - + return hasAttrib } - + // Logic tells us we now have a range on a single line - + var lineNum = selStart[0] , start = selStart[1] , end = selEnd[1] , hasAttrib = true - + // Iterate over attribs on this line - + var opIter = Changeset.opIterator(rep.alines[lineNum]) , indexIntoLine = 0 - + while (opIter.hasNext()) { var op = opIter.next(); var opStartInLine = indexIntoLine; @@ -2394,11 +2398,11 @@ function Ace2Inner(){ } indexIntoLine = opEndInLine; } - + return hasAttrib } } - + editorInfo.ace_getAttributeOnSelection = getAttributeOnSelection; function toggleAttributeOnSelection(attributeName) @@ -2423,6 +2427,9 @@ function Ace2Inner(){ var opIter = Changeset.opIterator(rep.alines[n]); var indexIntoLine = 0; var selectionStartInLine = 0; + if (documentAttributeManager.lineHasMarker(n)) { + selectionStartInLine = 1; // ignore "*" used as line marker + } var selectionEndInLine = rep.lines.atIndex(n).text.length; // exclude newline if (n == selStartLine) { @@ -3643,7 +3650,7 @@ function Ace2Inner(){ // Is caret potentially hidden by the chat button? var myselection = document.getSelection(); // get the current caret selection var caretOffsetTop = myselection.focusNode.parentNode.offsetTop | myselection.focusNode.offsetTop; // get the carets selection offset in px IE 214 - + if(myselection.focusNode.wholeText){ // Is there any content? If not lineHeight will report wrong.. var lineHeight = myselection.focusNode.parentNode.offsetHeight; // line height of populated links }else{ @@ -3715,13 +3722,13 @@ function Ace2Inner(){ // As ubuntu cannot use Alt F10.... // Focus on the editbar. -- TODO: Move Focus back to previous state (we know it so we can use it) var firstEditbarElement = parent.parent.$('#editbar').children("ul").first().children().first().children().first().children().first(); - $(this).blur(); + $(this).blur(); firstEditbarElement.focus(); evt.preventDefault(); } if ((!specialHandled) && altKey && keyCode == 67 && type === "keydown"){ // Alt c focuses on the Chat window - $(this).blur(); + $(this).blur(); parent.parent.chat.show(); parent.parent.$("#chatinput").focus(); evt.preventDefault(); @@ -4963,7 +4970,7 @@ function Ace2Inner(){ // Disabled: https://github.com/ether/etherpad-lite/issues/2546 // Will break OL re-numbering: https://github.com/ether/etherpad-lite/pull/2533 - // $(document).on("cut", handleCut); + // $(document).on("cut", handleCut); $(root).on("blur", handleBlur); if (browser.msie) @@ -4974,7 +4981,7 @@ function Ace2Inner(){ // Don't paste on middle click of links $(root).on("paste", function(e){ - // TODO: this breaks pasting strings into URLS when using + // TODO: this breaks pasting strings into URLS when using // Control C and Control V -- the Event is never available // here.. :( if(e.target.a || e.target.localName === "a"){ @@ -5349,8 +5356,9 @@ function Ace2Inner(){ function initLineNumbers() { lineNumbersShown = 1; - sideDiv.innerHTML = '<table border="0" cellpadding="0" cellspacing="0" align="right"><tr><td id="sidedivinner"><div>1</div></td></tr></table>'; + sideDiv.innerHTML = '<table border="0" cellpadding="0" cellspacing="0" align="right"><tr><td id="sidedivinner" class="sidedivinner"><div>1</div></td></tr></table>'; sideDivInner = outerWin.document.getElementById("sidedivinner"); + $(sideDiv).addClass("sidediv"); } function updateLineNumbers() diff --git a/src/static/js/admin/plugins.js b/src/static/js/admin/plugins.js index d337da03..48d1ab70 100644 --- a/src/static/js/admin/plugins.js +++ b/src/static/js/admin/plugins.js @@ -11,7 +11,7 @@ $(document).ready(function () { //connect var room = url + "pluginfw/installer"; - socket = io.connect(room, {resource : resource}); + socket = io.connect(room, {path: baseURL + "socket.io", resource : resource}); function search(searchTerm, limit) { if(search.searchTerm != searchTerm) { diff --git a/src/static/js/admin/settings.js b/src/static/js/admin/settings.js index 8a4473d6..42b038d5 100644 --- a/src/static/js/admin/settings.js +++ b/src/static/js/admin/settings.js @@ -10,7 +10,7 @@ $(document).ready(function () { //connect var room = url + "settings"; - socket = io.connect(room, {resource : resource}); + socket = io.connect(room, {path: baseURL + "socket.io", resource : resource}); socket.on('settings', function (settings) { diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index 5c1e8efb..6820da07 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -100,7 +100,7 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas function textify(str) { return sanitizeUnicode( - str.replace(/\n/g, '').replace(/[\n\r ]/g, ' ').replace(/\xa0/g, ' ').replace(/\t/g, ' ')); + str.replace(/(\n | \n)/g, ' ').replace(/[\n\r ]/g, ' ').replace(/\xa0/g, ' ').replace(/\t/g, ' ')); } function getAssoc(node, name) diff --git a/src/static/js/pad.js b/src/static/js/pad.js index 6ee39b6b..eb90a883 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.js @@ -465,8 +465,8 @@ var pad = { }, switchToPad: function(padId) { - var newHref = new RegExp(/.*\/p\/[^\/]+/).exec(document.location.pathname) || clientVars.padId; - newHref = newHref[0]; + var options = document.location.href.split('?')[1]; + var newHref = padId; if (typeof options != "undefined" && options != null){ newHref = newHref + '?' + options; } diff --git a/src/static/js/pad_editbar.js b/src/static/js/pad_editbar.js index e418969e..48fcaab4 100644 --- a/src/static/js/pad_editbar.js +++ b/src/static/js/pad_editbar.js @@ -185,9 +185,31 @@ var padeditbar = (function() this.commands[cmd] = callback; return this; }, + calculateEditbarHeight: function() { + // if we're on timeslider, there is nothing on editbar, so we just use zero + var onTimeslider = $('.menu_left').length === 0; + if (onTimeslider) return 0; + + // if editbar has both menu left and right, its height must be + // the max between the height of these two parts + var leftMenuPosition = $('.menu_left').offset().top; + var rightMenuPosition = $('.menu_right').offset().top; + var editbarHasMenuLeftAndRight = (leftMenuPosition === rightMenuPosition); + + var height; + if (editbarHasMenuLeftAndRight) { + height = Math.max($('.menu_left').height(), $('.menu_right').height()); + } + else { + height = $('.menu_left').height(); + } + + return height; + }, redrawHeight: function(){ - var editbarHeight = $('.menu_left').height() + 1 + "px"; - var containerTop = $('.menu_left').height() + 6 + "px"; + var minimunEditbarHeight = self.calculateEditbarHeight(); + var editbarHeight = minimunEditbarHeight + 1 + "px"; + var containerTop = minimunEditbarHeight + 6 + "px"; $('#editbar').css("height", editbarHeight); $('#editorcontainer').css("top", containerTop); diff --git a/tests/backend/specs/api/pad.js b/tests/backend/specs/api/pad.js index 14e99091..e04033c0 100644 --- a/tests/backend/specs/api/pad.js +++ b/tests/backend/specs/api/pad.js @@ -79,6 +79,8 @@ describe('Permission', function(){ -> movePad(newPadID, originalPadId) -- Should provide consistant pad data -> getText(originalPadId) -- Should be "hello world" -> getLastEdited(padID) -- Should not be 0 + -> appendText(padID, "hello") + -> getText(padID) -- Should be "hello worldhello" -> setHTML(padID) -- Should fail on invalid HTML -> setHTML(padID) *3 -- Should fail on invalid HTML -> getHTML(padID) -- Should return HTML close to posted HTML @@ -483,6 +485,30 @@ describe('getLastEdited', function(){ }); }) +describe('appendText', function(){ + it('Append text to a pad Id', function(done) { + api.get(endPoint('appendText', '1.2.13')+"&padID="+testPadId+"&text=hello") + .expect(function(res){ + if(res.body.code !== 0) throw new Error("Pad Append Text failed"); + }) + .expect('Content-Type', /json/) + .expect(200, done); + }); +}); + +describe('getText', function(){ + it('Gets text on a pad Id', function(done) { + api.get(endPoint('getText')+"&padID="+testPadId) + .expect(function(res){ + if(res.body.code !== 0) throw new Error("Pad Get Text failed"); + if(res.body.data.text !== text+"\nhello") throw new Error("Pad Text not set properly"); + }) + .expect('Content-Type', /json/) + .expect(200, done); + }); +}); + + describe('setHTML', function(){ it('Sets the HTML of a Pad attempting to pass ugly HTML', function(done) { var html = "<div><b>Hello HTML</title></head></div>"; @@ -542,8 +568,9 @@ describe('createPad', function(){ */ -var endPoint = function(point){ - return '/api/'+apiVersion+'/'+point+'?apikey='+apiKey; +var endPoint = function(point, version){ + version = version || apiVersion; + return '/api/'+version+'/'+point+'?apikey='+apiKey; } function makeid() diff --git a/tests/frontend/specs/indentation.js b/tests/frontend/specs/indentation.js index 8e851d87..71aafc7a 100644 --- a/tests/frontend/specs/indentation.js +++ b/tests/frontend/specs/indentation.js @@ -15,7 +15,7 @@ describe("indentation button", function(){ //select this text element $firstTextElement.sendkeys('{selectall}'); - if(inner$(window)[0].bowser.firefox || inner$(window)[0].bowser.modernIE){ // if it's a mozilla or IE + if(inner$(window)[0].bowser.firefox || inner$(window)[0].bowser.modernIE){ // if it's a mozilla or IE var evtType = "keypress"; }else{ var evtType = "keydown"; @@ -31,7 +31,7 @@ describe("indentation button", function(){ }); it("indent text with button", function(done){ - var inner$ = helper.padInner$; + var inner$ = helper.padInner$; var chrome$ = helper.padChrome$; var $indentButton = chrome$(".buttonicon-indent"); @@ -43,7 +43,7 @@ describe("indentation button", function(){ }); it("keeps the indent on enter for the new line", function(done){ - var inner$ = helper.padInner$; + var inner$ = helper.padInner$; var chrome$ = helper.padChrome$; var $indentButton = chrome$(".buttonicon-indent"); @@ -51,9 +51,9 @@ describe("indentation button", function(){ //type a bit, make a line break and type again var $firstTextElement = inner$("div span").first(); - $firstTextElement.sendkeys('line 1'); - $firstTextElement.sendkeys('{enter}'); - $firstTextElement.sendkeys('line 2'); + $firstTextElement.sendkeys('line 1'); + $firstTextElement.sendkeys('{enter}'); + $firstTextElement.sendkeys('line 2'); $firstTextElement.sendkeys('{enter}'); helper.waitFor(function(){ @@ -68,13 +68,83 @@ describe("indentation button", function(){ }); }); + it("indents text with spaces on enter if previous line ends with ':', '[', '(', or '{'", function(done){ + var inner$ = helper.padInner$; + var chrome$ = helper.padChrome$; + + //type a bit, make a line break and type again + var $firstTextElement = inner$("div").first(); + $firstTextElement.sendkeys("line with ':'{enter}"); + $firstTextElement.sendkeys("line with '['{enter}"); + $firstTextElement.sendkeys("line with '('{enter}"); + $firstTextElement.sendkeys("line with '{{}'{enter}"); + + helper.waitFor(function(){ + return inner$("div span").first().text().indexOf("line with '{'") === -1; + }).done(function(){ + // we validate bottom to top for easier implementation + + // curly braces + var $lineWithCurlyBraces = inner$("div").first().next().next().next(); + $lineWithCurlyBraces.sendkeys('{{}'); + pressEnter(); // cannot use sendkeys('{enter}') here, browser does not read the command properly + var $lineAfterCurlyBraces = inner$("div").first().next().next().next().next(); + expect($lineAfterCurlyBraces.text()).to.match(/\s{4}/); // tab === 4 spaces + + // parenthesis + var $lineWithParenthesis = inner$("div").first().next().next(); + $lineWithParenthesis.sendkeys('('); + pressEnter(); + var $lineAfterParenthesis = inner$("div").first().next().next().next(); + expect($lineAfterParenthesis.text()).to.match(/\s{4}/); + + // bracket + var $lineWithBracket = inner$("div").first().next(); + $lineWithBracket.sendkeys('['); + pressEnter(); + var $lineAfterBracket = inner$("div").first().next().next(); + expect($lineAfterBracket.text()).to.match(/\s{4}/); + + // colon + var $lineWithColon = inner$("div").first(); + $lineWithColon.sendkeys(':'); + pressEnter(); + var $lineAfterColon = inner$("div").first().next(); + expect($lineAfterColon.text()).to.match(/\s{4}/); + + done(); + }); + }); + + it("appends indentation to the indent of previous line if previous line ends with ':', '[', '(', or '{'", function(done){ + var inner$ = helper.padInner$; + var chrome$ = helper.padChrome$; + + //type a bit, make a line break and type again + var $firstTextElement = inner$("div").first(); + $firstTextElement.sendkeys(" line with some indentation and ':'{enter}"); + $firstTextElement.sendkeys("line 2{enter}"); + + helper.waitFor(function(){ + return inner$("div span").first().text().indexOf("line 2") === -1; + }).done(function(){ + var $lineWithColon = inner$("div").first(); + $lineWithColon.sendkeys(':'); + pressEnter(); + var $lineAfterColon = inner$("div").first().next(); + expect($lineAfterColon.text()).to.match(/\s{6}/); // previous line indentation + regular tab (4 spaces) + + done(); + }); + }); + /* it("makes text indented and outdented", function() { //get the inner iframe var $inner = testHelper.$getPadInner(); - + //get the first text element out of the inner iframe var firstTextElement = $inner.find("div").first(); @@ -87,7 +157,7 @@ describe("indentation button", function(){ //ace creates a new dom element when you press a button, so just get the first text element again var newFirstTextElement = $inner.find("div").first(); - + // is there a list-indent class element now? var firstChild = newFirstTextElement.children(":first"); var isUL = firstChild.is('ul'); @@ -160,12 +230,12 @@ describe("indentation button", function(){ /* this test creates the below content, both should have double indentation line1 line2 - + firstTextElement.sendkeys('{rightarrow}'); // simulate a keypress of enter firstTextElement.sendkeys('{enter}'); // simulate a keypress of enter firstTextElement.sendkeys('line 1'); // simulate writing the first line - firstTextElement.sendkeys('{enter}'); // simulate a keypress of enter + firstTextElement.sendkeys('{enter}'); // simulate a keypress of enter firstTextElement.sendkeys('line 2'); // simulate writing the second line //get the second text element out of the inner iframe @@ -203,3 +273,15 @@ describe("indentation button", function(){ });*/ }); + +function pressEnter(){ + var inner$ = helper.padInner$; + if(inner$(window)[0].bowser.firefox || inner$(window)[0].bowser.modernIE){ // if it's a mozilla or IE + var evtType = "keypress"; + }else{ + var evtType = "keydown"; + } + var e = inner$.Event(evtType); + e.keyCode = 13; // enter :| + inner$("#innerdocbody").trigger(e); +} |