diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e3a5d77f..c1b38380a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,15 @@ * NEW: Plugins can now provide their own frontend tests * NEW: Improved server-side logging * NEW: Admin dashboard mobile device support and new hooks for Admin dashboard + * NEW: Get current API version from API + * Fix: Text Export indentation now supports multiple indentations * Fix: Bugfix getChatHistory API method + * Fix: Stop Chrome losing caret after paste is texted + * Fix: Make colons on end of line create 4 spaces on indent + * Fix: Make indent when on middle of the line stop creating list + * Fix: Stop long strings breaking the UX by moving focus away from beginning of line + * Fix: padUsersCount no longer hangs server + * Fix: Issue with two part locale specs not working * Fix: Make plugin search case insensitive * Fix: Indentation and bullets on text export * Fix: Resolve various warnings on dependencies during install diff --git a/README.md b/README.md index fa0da3639..91410b873 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ documented codebase makes it easier for developers to improve the code and contr Etherpad Lite 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. There is also a [jQuery plugin](https://github.com/johnyma22/etherpad-lite-jquery-plugin) that helps you to embed Pads into your website. +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. There is also a [jQuery plugin](https://github.com/ether/etherpad-lite-jquery-plugin) that helps you to embed Pads into your website. There's also a full-featured plugin framework, allowing you to easily add your own features. Finally, Etherpad Lite comes with translations into tons of different languages! @@ -108,7 +108,7 @@ You know all this and just want to know how you can help? Look at the [TODO list](https://github.com/ether/etherpad-lite/wiki/TODO) and our [Issue tracker](https://github.com/ether/etherpad-lite/issues). (Please consider using [jshint](http://www.jshint.com/about/), if you plan to contribute code.) -Also, and most importantly, read our [**Developer Guidelines**](https://github.com/ether/etherpad-lite/wiki/Developer-Guidelines), really! +Also, and most importantly, read our [**Developer Guidelines**](https://github.com/ether/etherpad-lite/blob/master/CONTRIBUTING.md), really! # Get in touch Join the [mailinglist](http://groups.google.com/group/etherpad-lite-dev) and make some noise on our freenode irc channel [#etherpad-lite-dev](http://webchat.freenode.net?channels=#etherpad-lite-dev)! diff --git a/bin/extractPadData.js b/bin/extractPadData.js index ea00f953a..0d1acaaee 100644 --- a/bin/extractPadData.js +++ b/bin/extractPadData.js @@ -35,7 +35,8 @@ async.series([ function(callback) { settings = require('../src/node/utils/Settings'); db = require('../src/node/db/DB'); - dirty = require("../src/node_modules/ueberDB/node_modules/dirty")(padId + ".db"); + dirty = require("../node_modules/ep_etherpad-lite/node_modules/ueberDB/node_modules/dirty")(padId + ".db"); + callback(); }, //intallize the database function (callback) @@ -45,7 +46,7 @@ async.series([ //get the pad function (callback) { - padManager = require('../node/db/PadManager'); + padManager = require('../src/node/db/PadManager'); padManager.getPad(padId, function(err, _pad) { @@ -82,7 +83,10 @@ async.series([ db.db.db.wrappedDB.get(dbkey, function(err, dbvalue) { if(err) { callback(err); return} - dbvalue=JSON.parse(dbvalue); + + if(typeof dbvalue != 'object'){ + dbvalue=JSON.parse(dbvalue); // if its not json then parse it as json + } dirty.set(dbkey, dbvalue, callback); }); diff --git a/src/locales/bn.json b/src/locales/bn.json index dba1c2154..2d12528b3 100644 --- a/src/locales/bn.json +++ b/src/locales/bn.json @@ -2,26 +2,60 @@ "@metadata": { "authors": [ "Bellayet", - "Nasir8891" + "Nasir8891", + "Sankarshan" ] }, "index.newPad": "\u09a8\u09a4\u09c1\u09a8 \u09aa\u09cd\u09af\u09be\u09a1", "index.createOpenPad": "\u0985\u09a5\u09ac\u09be \u09a8\u09be\u09ae \u09b2\u09bf\u0996\u09c7 \u09aa\u09cd\u09af\u09be\u09a1 \u0996\u09c1\u09b2\u09c1\u09a8\/\u09a4\u09c8\u09b0\u09c0 \u0995\u09b0\u09c1\u09a8:", "pad.toolbar.bold.title": "\u0997\u09be\u09a1\u09bc \u0995\u09b0\u09be (Ctrl-B)", "pad.toolbar.italic.title": "\u09ac\u09be\u0981\u0995\u09be \u0995\u09b0\u09be (Ctrl-I)", + "pad.toolbar.underline.title": "\u0986\u09a8\u09cd\u09a1\u09be\u09b0\u09b2\u09be\u0987\u09a8 (Ctrl-U)", + "pad.toolbar.ol.title": "\u09b8\u09be\u09b0\u09bf\u09ac\u09a6\u09cd\u09a7 \u09a4\u09be\u09b2\u09bf\u0995\u09be", "pad.toolbar.indent.title": "\u09aa\u09cd\u09b0\u09be\u09a8\u09cd\u09a4\u09bf\u0995\u0995\u09b0\u09a3", + "pad.toolbar.unindent.title": "\u0986\u0989\u099f\u09a1\u09c7\u09a8\u09cd\u099f", + "pad.toolbar.undo.title": "\u09ac\u09be\u09a4\u09bf\u09b2 \u0995\u09b0\u09c1\u09a8 (Ctrl-Z)", + "pad.toolbar.redo.title": "\u09aa\u09c1\u09a8\u09b0\u09be\u09af\u09bc \u0995\u09b0\u09c1\u09a8 (Ctrl-Y)", + "pad.toolbar.clearAuthorship.title": "\u0995\u09c3\u09a4\u09bf \u09b0\u0982 \u09aa\u09b0\u09bf\u09b7\u09cd\u0995\u09be\u09b0 \u0995\u09b0\u09c1\u09a8", + "pad.toolbar.timeslider.title": "\u099f\u09be\u0987\u09ae\u09b8\u09cd\u09b2\u09be\u0987\u09a1\u09be\u09b0", + "pad.toolbar.savedRevision.title": "\u09b8\u0982\u09b8\u09cd\u0995\u09b0\u09a3 \u09b8\u0982\u09b0\u0995\u09cd\u09b7\u09a3 \u0995\u09b0\u09c1\u09a8", "pad.toolbar.settings.title": "\u09b8\u09c7\u099f\u09bf\u0982", + "pad.toolbar.embed.title": "\u098f\u0987 \u09aa\u09cd\u09af\u09be\u09a1-\u099f\u09bf \u098f\u09ae\u09cd\u09ac\u09c7\u09a1 \u0995\u09b0\u09c1\u09a8", + "pad.toolbar.showusers.title": "\u098f\u0987 \u09aa\u09cd\u09af\u09be\u09a1\u09c7\u09b0 \u09ac\u09cd\u09af\u09ac\u09b9\u09be\u09b0\u0995\u09be\u09b0\u09c0\u09a6\u09c7\u09b0 \u09a6\u09c7\u0996\u09be\u09a8", "pad.colorpicker.save": "\u09b8\u0982\u09b0\u0995\u09cd\u09b7\u09a3", "pad.colorpicker.cancel": "\u09ac\u09be\u09a4\u09bf\u09b2", "pad.loading": "\u09b2\u09cb\u09a1\u09bf\u0982...", + "pad.passwordRequired": "\u098f\u0987 \u09aa\u09cd\u09af\u09be\u09a1-\u099f\u09bf \u09a6\u09c7\u0996\u09be\u09b0 \u099c\u09a8\u09cd\u09af \u0986\u09aa\u09a8\u09be\u0995\u09c7 \u09aa\u09be\u09b8\u0993\u09af\u09bc\u09be\u09b0\u09cd\u09a1 \u09ac\u09cd\u09af\u09ac\u09b9\u09be\u09b0 \u0995\u09b0\u09a4\u09c7 \u09b9\u09ac\u09c7", + "pad.permissionDenied": "\u09a6\u09c1\u0983\u0996\u09bf\u09a4, \u098f \u09aa\u09cd\u09af\u09be\u09a1-\u099f\u09bf \u09a6\u09c7\u0996\u09be\u09b0 \u0985\u09a7\u09bf\u0995\u09be\u09b0 \u0986\u09aa\u09a8\u09be\u09b0 \u09a8\u09c7\u0987", + "pad.wrongPassword": "\u0986\u09aa\u09a8\u09be\u09b0 \u09aa\u09be\u09b8\u0993\u09af\u09bc\u09be\u09b0\u09cd\u09a1 \u09b8\u09a0\u09bf\u0995 \u09a8\u09af\u09bc", + "pad.settings.padSettings": "\u09aa\u09cd\u09af\u09be\u09a1\u09c7\u09b0 \u09b8\u09cd\u09a5\u09be\u09aa\u09a8", + "pad.settings.myView": "\u0986\u09ae\u09be\u09b0 \u09a6\u09c3\u09b6\u09cd\u09af", + "pad.settings.stickychat": "\u099a\u09cd\u09af\u09be\u099f \u09b8\u0995\u09cd\u09b0\u09c0\u09a8\u09c7 \u09aa\u09cd\u09b0\u09a6\u09b0\u09cd\u09b6\u09a8 \u0995\u09b0\u09be \u09b9\u09ac\u09c7", + "pad.settings.colorcheck": "\u09b2\u09c7\u0996\u0995\u09a6\u09c7\u09b0 \u09a8\u09bf\u099c\u09b8\u09cd\u09ac \u09a8\u09bf\u09b0\u09cd\u09ac\u09be\u099a\u09bf\u09a4 \u09b0\u0982", + "pad.settings.linenocheck": "\u09b2\u09be\u0987\u09a8 \u09a8\u09ae\u09cd\u09ac\u09b0", + "pad.settings.fontType": "\u09ab\u09a8\u09cd\u099f-\u098f\u09b0 \u09aa\u09cd\u09b0\u0995\u09be\u09b0:", "pad.settings.fontType.normal": "\u09b8\u09be\u09a7\u09be\u09b0\u09a3", + "pad.settings.fontType.monospaced": "Monospace", + "pad.settings.globalView": "\u09b8\u09b0\u09cd\u09ac\u09ac\u09cd\u09af\u09be\u09aa\u09c0 \u09a6\u09c3\u09b6\u09cd\u09af", "pad.settings.language": "\u09ad\u09be\u09b7\u09be:", + "pad.importExport.import_export": "\u0987\u09ae\u09cd\u09aa\u09cb\u09b0\u099f\/\u098f\u0995\u09cd\u09b8\u09aa\u09cb\u09b0\u09cd\u099f", + "pad.importExport.import": "\u0995\u09cb\u09a8 \u099f\u09c7\u0995\u09cd\u09b8\u099f \u09ab\u09be\u0987\u09b2 \u09ac\u09be \u09a1\u0995\u09c1\u09ae\u09c7\u09a8\u09cd\u099f \u0986\u09aa\u09b2\u09cb\u09a1 \u0995\u09b0\u09c1\u09a8", + "pad.importExport.importSuccessful": "\u09b8\u09ab\u09b2!", "pad.importExport.export": "\u098f\u0987 \u09aa\u09cd\u09af\u09be\u09a1\u099f\u09bf \u098f\u0995\u09cd\u09b8\u09aa\u09cb\u09b0\u09cd\u099f \u0995\u09b0\u09c1\u09a8", "pad.importExport.exporthtml": "\u098f\u0987\u099a\u099f\u09bf\u098f\u09ae\u098f\u09b2", "pad.importExport.exportplain": "\u09b8\u09be\u09a7\u09be\u09b0\u09a3 \u09b2\u09c7\u0996\u09be", "pad.importExport.exportword": "\u09ae\u09be\u0987\u0995\u09cd\u09b0\u09cb\u09b8\u09ab\u099f \u0993\u09af\u09bc\u09be\u09b0\u09cd\u09a1", "pad.importExport.exportpdf": "\u09aa\u09bf\u09a1\u09bf\u098f\u09ab", "pad.importExport.exportopen": "\u0993\u09a1\u09bf\u098f\u09ab (\u0993\u09aa\u09c7\u09a8 \u09a1\u0995\u09c1\u09ae\u09c7\u09a8\u09cd\u099f \u09ab\u09b0\u09ae\u09cd\u09af\u09be\u099f)", + "pad.importExport.exportdokuwiki": "DokuWiki", + "pad.modals.connected": "\u09af\u09cb\u0997\u09be\u09af\u09cb\u0997 \u09b8\u09ab\u09b2", + "pad.modals.reconnecting": "\u0986\u09aa\u09a8\u09be\u09b0 \u09aa\u09cd\u09af\u09be\u09a1\u09c7\u09b0 \u09b8\u09be\u09a5\u09c7 \u09b8\u0982\u09af\u09cb\u0997\u09b8\u09cd\u09a5\u09be\u09aa\u09a8 \u0995\u09b0\u09be \u09b9\u099a\u09cd\u099b\u09c7..", + "pad.modals.forcereconnect": "\u09aa\u09c1\u09a8\u09b0\u09be\u09af\u09bc \u09b8\u0982\u09af\u09cb\u0997\u09b8\u09cd\u09a5\u09be\u09aa\u09a8\u09c7\u09b0 \u099a\u09c7\u09b7\u09cd\u099f\u09be", + "pad.modals.userdup": "\u0985\u09a8\u09cd\u09af \u0989\u0987\u09a8\u09cd\u09a1\u09cb-\u09a4\u09c7 \u0996\u09cb\u09b2\u09be \u09b9\u09af\u09bc\u09c7\u099b\u09c7", + "pad.modals.unauth": "\u0986\u09aa\u09a8\u09be\u09b0 \u0985\u09a7\u09bf\u0995\u09be\u09b0 \u09a8\u09c7\u0987", + "pad.modals.looping": "\u09af\u09cb\u0997\u09be\u09af\u09cb\u0997 \u09ac\u09bf\u099a\u09cd\u099b\u09bf\u09a8\u09cd\u09a8", + "pad.modals.initsocketfail": "\u09b8\u09be\u09b0\u09cd\u09ad\u09be\u09b0-\u098f\u09b0 \u09b8\u09be\u09a5\u09c7 \u09af\u09cb\u0997\u09be\u09af\u09cb\u0997 \u0995\u09b0\u09a4\u09c7 \u0985\u09b8\u0995\u09cd\u09b7\u09ae\u0964", + "pad.modals.slowcommit": "\u09af\u09cb\u0997\u09be\u09af\u09cb\u0997 \u09ac\u09bf\u099a\u09cd\u099b\u09bf\u09a8\u09cd\u09a8", "pad.modals.deleted": "\u0985\u09aa\u09b8\u09be\u09b0\u09bf\u09a4\u0964", "pad.modals.deleted.explanation": "\u098f\u0987 \u09aa\u09cd\u09af\u09be\u09a1\u099f\u09bf \u0985\u09aa\u09b8\u09be\u09b0\u09a3 \u0995\u09b0\u09be \u09b9\u09af\u09bc\u09c7\u099b\u09c7\u0964", "pad.modals.disconnected.explanation": "\u09b8\u09be\u09b0\u09cd\u09ad\u09be\u09b0\u09c7\u09b0 \u09b8\u09be\u09a5\u09c7 \u09af\u09cb\u0997\u09be\u09af\u09cb\u0997 \u0995\u09b0\u09be \u09af\u09be\u099a\u09cd\u099b\u09c7 \u09a8\u09be", @@ -34,6 +68,7 @@ "timeslider.toolbar.authors": "\u09b2\u09c7\u0996\u0995\u0997\u09a3:", "timeslider.toolbar.authorsList": "\u0995\u09cb\u09a8\u09cb \u09b2\u09c7\u0996\u0995 \u09a8\u09c7\u0987", "timeslider.exportCurrent": "\u09ac\u09b0\u09cd\u09a4\u09ae\u09be\u09a8 \u09b8\u0982\u09b8\u09cd\u0995\u09b0\u09a3\u099f\u09bf \u098f\u0995\u09cd\u09b8\u09aa\u09cb\u09b0\u09cd\u099f \u0995\u09b0\u09c1\u09a8:", + "timeslider.dateformat": "{{month}}\/{{day}}\/{{year}} {{hours}}:{{minutes}}:{{seconds}}", "timeslider.month.january": "\u099c\u09be\u09a8\u09c1\u09af\u09bc\u09be\u09b0\u09bf", "timeslider.month.february": "\u09ab\u09c7\u09ac\u09cd\u09b0\u09c1\u09af\u09bc\u09be\u09b0\u09bf", "timeslider.month.march": "\u09ae\u09be\u09b0\u09cd\u099a", @@ -45,5 +80,12 @@ "timeslider.month.september": "\u09b8\u09c7\u09aa\u09cd\u099f\u09c7\u09ae\u09cd\u09ac\u09b0", "timeslider.month.october": "\u0985\u0995\u09cd\u099f\u09cb\u09ac\u09b0", "timeslider.month.november": "\u09a8\u09ad\u09c7\u09ae\u09cd\u09ac\u09b0", - "timeslider.month.december": "\u09a1\u09bf\u09b8\u09c7\u09ae\u09cd\u09ac\u09b0" + "timeslider.month.december": "\u09a1\u09bf\u09b8\u09c7\u09ae\u09cd\u09ac\u09b0", + "pad.userlist.entername": "\u0986\u09aa\u09a8\u09be\u09b0 \u09a8\u09be\u09ae", + "pad.userlist.unnamed": "\u0995\u09cb\u09a8 \u09a8\u09be\u09ae \u09a8\u09bf\u09b0\u09cd\u09ac\u09be\u099a\u09a8 \u0995\u09b0\u09be \u09b9\u09af\u09bc\u09a8\u09bf", + "pad.userlist.guest": "\u0985\u09a4\u09bf\u09a5\u09bf", + "pad.userlist.approve": "\u0985\u09a8\u09c1\u09ae\u09cb\u09a6\u09bf\u09a4", + "pad.impexp.importbutton": "\u098f\u0996\u09a8 \u0987\u09ae\u09cd\u09aa\u09cb\u09b0\u09cd\u099f \u0995\u09b0\u09c1\u09a8", + "pad.impexp.importing": "\u0987\u09ae\u09cd\u09aa\u09cb\u09b0\u09cd\u099f \u099a\u09b2\u099b\u09c7...", + "pad.impexp.importfailed": "\u0987\u09ae\u09cd\u09aa\u09cb\u09b0\u09cd\u099f \u0985\u09b8\u0995\u09cd\u09b7\u09ae" } \ No newline at end of file diff --git a/src/locales/br.json b/src/locales/br.json index d53ce591b..1197f0d65 100644 --- a/src/locales/br.json +++ b/src/locales/br.json @@ -1,34 +1,96 @@ { + "@metadata": { + "authors": [ + "Fohanno", + "Fulup", + "Gwenn-Ael", + "Y-M D" + ] + }, "index.newPad": "Pad nevez", "index.createOpenPad": "pe kroui\u00f1\/digeri\u00f1 ur pad gant an anv :", "pad.toolbar.bold.title": "Tev (Ctrl-B)", "pad.toolbar.italic.title": "Italek (Ctrl-I)", "pad.toolbar.underline.title": "Islinenna\u00f1 (Ctrl-U)", + "pad.toolbar.strikethrough.title": "Barrennet", + "pad.toolbar.ol.title": "Roll urzhiet", + "pad.toolbar.ul.title": "Roll en dizurzh", "pad.toolbar.indent.title": "Endanta\u00f1", "pad.toolbar.unindent.title": "Diendanta\u00f1", + "pad.toolbar.undo.title": "Dizober (Ktrl-Z)", + "pad.toolbar.redo.title": "Adober (Ktrl-Y)", + "pad.toolbar.clearAuthorship.title": "Diverka\u00f1 al livio\u00f9 oc'h anaout an aozerien", + "pad.toolbar.import_export.title": "Enporzhia\u00f1\/Ezporzhia\u00f1 eus\/war-zu ur furmad restr dishe\u00f1vel", + "pad.toolbar.timeslider.title": "Istor dinamek", + "pad.toolbar.savedRevision.title": "Doareo\u00f9 enrollet", "pad.toolbar.settings.title": "Arventenno\u00f9", + "pad.toolbar.embed.title": "Enframma\u00f1 ar pad-ma\u00f1", + "pad.toolbar.showusers.title": "Diskwelet implijerien ar Pad", "pad.colorpicker.save": "Enrolla\u00f1", "pad.colorpicker.cancel": "Nulla\u00f1", "pad.loading": "O karga\u00f1...", + "pad.passwordRequired": "Ezhomm ho peus ur ger-tremen evit mont d'ar Pad-se", + "pad.permissionDenied": "\nN'oc'h ket aotreet da vont d'ar pad-ma\u00f1", + "pad.wrongPassword": "Fazius e oa ho ker-tremen", + "pad.settings.padSettings": "Arventenno\u00f9 Pad", "pad.settings.myView": "Ma diskwel", + "pad.settings.stickychat": "Diskwel ar flap bepred", + "pad.settings.colorcheck": "Livio\u00f9 anaout", + "pad.settings.linenocheck": "Niverenno\u00f9 linenno\u00f9", + "pad.settings.fontType": "Seurt font :", "pad.settings.fontType.normal": "Reizh", + "pad.settings.fontType.monospaced": "Monospas", + "pad.settings.globalView": "Gwel dre vras", "pad.settings.language": "Yezh :", "pad.importExport.import_export": "Enporzhia\u00f1\/Ezporzhia\u00f1", "pad.importExport.import": "Enkarga\u00f1 un destenn pe ur restr", + "pad.importExport.importSuccessful": "Deuet eo ganeoc'h !", + "pad.importExport.export": "Ezporzhia\u00f1 ar pad brema\u00f1 evel :", "pad.importExport.exporthtml": "HTML", "pad.importExport.exportplain": "Testenn blaen", "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", "pad.importExport.exportdokuwiki": "DokuWiki", + "pad.importExport.abiword.innerHTML": "Ne c'hallit ket emporzjia\u00f1 furmado\u00f9 testenno\u00f9 kriz pe html. Evit arc'hwelio\u00f9 enporzhia\u00f1 emdroetoc'h, staliit abiword<\/a> mar plij.", "pad.modals.connected": "Kevreet.", + "pad.modals.reconnecting": "Adkevrea\u00f1 war-zu ho pad...", + "pad.modals.forcereconnect": "Adkevrea\u00f1 dre heg", + "pad.modals.userdup": "Digor en ur prenestr all", + "pad.modals.userdup.explanation": "Digor eo ho pad, war a seblant, e meur a brenestr eus ho merdeer en urzhiataer-ma\u00f1.", + "pad.modals.userdup.advice": "Kevrea\u00f1 en ur implijout ar prenestr-ma\u00f1.", + "pad.modals.unauth": "N'eo ket aotreet", + "pad.modals.unauth.explanation": "Kemmet e vo hoc'h aotreo\u00f9 pa vo diskwelet ar bajenn.-ma\u00f1 Klaskit kevrea\u00f1 en-dro.", + "pad.modals.looping": "Digevreet.", + "pad.modals.looping.explanation": "Kudenno\u00f9 kehenti\u00f1 zo gant ar servijer sinkronelekaat.", + "pad.modals.looping.cause": "Posupl eo e vefe gwarezet ho kevreadur gant ur maltouter diembreget pe ur servijer proksi", + "pad.modals.initsocketfail": "Ne c'haller ket tizhout ar servijer.", + "pad.modals.initsocketfail.explanation": "Ne c'haller ket kevrea\u00f1 ouzh ar servijer sinkronelaat.", + "pad.modals.initsocketfail.cause": "Gallout a ra ar gudenn dont eus ho merdeer Web pe eus ho kevreadur Internet.", "pad.modals.slowcommit": "Digevreet.", + "pad.modals.slowcommit.explanation": "Ne respont ket ar serveur.", + "pad.modals.slowcommit.cause": "Gallout a ra dont diwar kudenno\u00f9 kevrea\u00f1 gant ar rouedad.", + "pad.modals.deleted": "Dilamet.", + "pad.modals.deleted.explanation": "Lamet eo bet ar pad-ma\u00f1.", + "pad.modals.disconnected": "Digevreet oc'h bet.", + "pad.modals.disconnected.explanation": "Kollet eo bet ar c'hevreadur gant ar servijer", + "pad.modals.disconnected.cause": "Dizimplijadus eo ar servijer marteze. Kelaouit ac'hanomp ma pad ar gudenn.", + "pad.share": "Ranna\u00f1 ar pad-ma\u00f1.", "pad.share.readonly": "Lenn hepken", "pad.share.link": "Liamm", + "pad.share.emebdcode": "Enframma\u00f1 an URL", "pad.chat": "Flap", + "pad.chat.title": "Digeri\u00f1 ar flap kevelet gant ar pad-ma\u00f1.", + "pad.chat.loadmessages": "Karga\u00f1 muioc'h a gemennadenno\u00f9", + "timeslider.pageTitle": "Istor dinamek eus {{appTitle}}", + "timeslider.toolbar.returnbutton": "Distrei\u00f1 d'ar pad-ma\u00f1.", "timeslider.toolbar.authors": "Aozerien :", "timeslider.toolbar.authorsList": "Aozer ebet", "timeslider.toolbar.exportlink.title": "Ezporzhia\u00f1", + "timeslider.exportCurrent": "Ezporzhia\u00f1 an doare brema\u00f1 evel :", + "timeslider.version": "Stumm {{version}}", + "timeslider.saved": "Enrolla\u00f1 {{day}} {{month}} {{year}}", + "timeslider.dateformat": "{{month}}\/{{day}}\/{{year}} {{hours}}:{{minutes}}:{{seconds}}", "timeslider.month.january": "Genver", "timeslider.month.february": "C'hwevrer", "timeslider.month.march": "Meurzh", @@ -41,16 +103,21 @@ "timeslider.month.october": "Here", "timeslider.month.november": "Du", "timeslider.month.december": "Kerzu", + "timeslider.unnamedauthor": "{{niver}} aozer dianav", + "timeslider.unnamedauthors": "Aozerien dianav", + "pad.savedrevs.marked": "Merket eo an adweladenn-ma\u00f1 evel adweladenn gwiriet", + "pad.userlist.entername": "Ebarzhit hoc'h anv", + "pad.userlist.unnamed": "dizanv", "pad.userlist.guest": "Den pedet", "pad.userlist.deny": "Nac'h", "pad.userlist.approve": "Aproui\u00f1", + "pad.editbar.clearcolors": "Diverka\u00f1 al livio\u00f9 stag ouzh an aozerien en teul a-bezh ?", "pad.impexp.importbutton": "Enporzhia\u00f1 brema\u00f1", "pad.impexp.importing": "Oc'h enporzhia\u00f1...", + "pad.impexp.confirmimport": "Ma vez enporzhiet ur restr e vo diverket ar pezh zo en teul a-vrema\u00f1. Ha sur oc'h e fell deoc'h mont betek penn ?", + "pad.impexp.convertFailed": "N'eus ket bet gallet enporzhia\u00f1 ar restr. Ober gant ur furmad teul all pe eila\u00f1\/pega\u00f1 gant an dorn.", + "pad.impexp.uploadFailed": "C'hwitet eo bet an enporzhia\u00f1. Klaskit en-dro.", "pad.impexp.importfailed": "C'hwitet eo an enporzhiadenn", - "@metadata": { - "authors": [ - "Fulup", - "Y-M D" - ] - } + "pad.impexp.copypaste": "Eilit\/pegit, mar plij", + "pad.impexp.exportdisabled": "Diweredekaet eo ezporzhia\u00f1 d'ar furmad {{type}}. Kit e darempred gant merour ar reizhiad evit gouzout hiroc'h." } \ No newline at end of file diff --git a/src/locales/de.json b/src/locales/de.json index 7c51fa912..209384222 100644 --- a/src/locales/de.json +++ b/src/locales/de.json @@ -22,7 +22,7 @@ "pad.toolbar.clearAuthorship.title": "Autorenfarben zur\u00fccksetzen", "pad.toolbar.import_export.title": "Import\/Export in verschiedenen Dateiformaten", "pad.toolbar.timeslider.title": "Pad-Versionsgeschichte anzeigen", - "pad.toolbar.savedRevision.title": "Diese Revision markieren", + "pad.toolbar.savedRevision.title": "Version speichern", "pad.toolbar.settings.title": "Einstellungen", "pad.toolbar.embed.title": "Dieses Pad teilen oder einbetten", "pad.toolbar.showusers.title": "Aktuell verbundene Benutzer anzeigen", diff --git a/src/locales/fi.json b/src/locales/fi.json index 50fd444d0..eeb4cb160 100644 --- a/src/locales/fi.json +++ b/src/locales/fi.json @@ -24,7 +24,7 @@ "pad.toolbar.clearAuthorship.title": "Poista kirjoittajav\u00e4rit", "pad.toolbar.import_export.title": "Tuo tai vie eri tiedostomuodoista tai -muotoihin", "pad.toolbar.timeslider.title": "Aikajana", - "pad.toolbar.savedRevision.title": "Tallennetut versiot", + "pad.toolbar.savedRevision.title": "Tallenna muutos", "pad.toolbar.settings.title": "Asetukset", "pad.toolbar.embed.title": "Upota muistio", "pad.toolbar.showusers.title": "N\u00e4yt\u00e4 muistion k\u00e4ytt\u00e4j\u00e4t", diff --git a/src/locales/nl.json b/src/locales/nl.json index 9b1c773bf..4142cc339 100644 --- a/src/locales/nl.json +++ b/src/locales/nl.json @@ -19,7 +19,7 @@ "pad.toolbar.clearAuthorship.title": "Kleuren auteurs wissen", "pad.toolbar.import_export.title": "Naar\/van andere opmaak exporteren\/importeren", "pad.toolbar.timeslider.title": "Tijdlijn", - "pad.toolbar.savedRevision.title": "Opgeslagen versies", + "pad.toolbar.savedRevision.title": "Versie opslaan", "pad.toolbar.settings.title": "Instellingen", "pad.toolbar.embed.title": "Pad insluiten", "pad.toolbar.showusers.title": "Gebruikers van dit pad weergeven", diff --git a/src/locales/pl.json b/src/locales/pl.json index 3481cafc5..6a46dd778 100644 --- a/src/locales/pl.json +++ b/src/locales/pl.json @@ -21,7 +21,7 @@ "pad.toolbar.clearAuthorship.title": "Usu\u0144 kolory autor\u00f3w", "pad.toolbar.import_export.title": "Import\/eksport z\/do r\u00f3\u017cnych format\u00f3w plik\u00f3w", "pad.toolbar.timeslider.title": "O\u015b czasu", - "pad.toolbar.savedRevision.title": "Zapisane wersje", + "pad.toolbar.savedRevision.title": "Zapisz wersj\u0119", "pad.toolbar.settings.title": "Ustawienia", "pad.toolbar.embed.title": "Umie\u015b\u0107 ten Notatnik", "pad.toolbar.showusers.title": "Poka\u017c u\u017cytkownik\u00f3w", @@ -102,6 +102,8 @@ "timeslider.month.october": "Pa\u017adziernik", "timeslider.month.november": "Listopad", "timeslider.month.december": "Grudzie\u0144", + "timeslider.unnamedauthor": "{{num}} nienazwany autor", + "timeslider.unnamedauthors": "{{num}} autor\u00f3w bez nazw", "pad.savedrevs.marked": "Ta wersja zosta\u0142a w\u0142a\u015bnie oznaczona jako zapisana.", "pad.userlist.entername": "Wprowad\u017a swoj\u0105 nazw\u0119", "pad.userlist.unnamed": "bez nazwy", diff --git a/src/locales/zh-hans.json b/src/locales/zh-hans.json index a17358266..a98ebfce6 100644 --- a/src/locales/zh-hans.json +++ b/src/locales/zh-hans.json @@ -3,6 +3,7 @@ "authors": [ "Dimension", "Hydra", + "Yfdyh000", "\u4e4c\u62c9\u8de8\u6c2a", "\u71c3\u7389" ] @@ -19,6 +20,7 @@ "pad.toolbar.undo.title": "\u64a4\u6d88 (Ctrl-Z)", "pad.toolbar.redo.title": "\u91cd\u505a (Ctrl-Y)", "pad.toolbar.clearAuthorship.title": "\u6e05\u9664\u4f5c\u540d\u989c\u8272", + "pad.toolbar.import_export.title": "\u4ee5\u5176\u4ed6\u6587\u4ef6\u683c\u5f0f\u5bfc\u5165\/\u5bfc\u51fa", "pad.toolbar.timeslider.title": "\u65f6\u95f4\u8f74", "pad.toolbar.savedRevision.title": "\u4fdd\u5b58\u4fee\u8ba2", "pad.toolbar.settings.title": "\u8bbe\u7f6e", diff --git a/src/locales/zh-hant.json b/src/locales/zh-hant.json index 7c01860e0..efe4da617 100644 --- a/src/locales/zh-hant.json +++ b/src/locales/zh-hant.json @@ -20,7 +20,7 @@ "pad.toolbar.clearAuthorship.title": "\u6e05\u9664\u4f5c\u540d\u984f\u8272", "pad.toolbar.import_export.title": "\u4ee5\u5176\u4ed6\u6a94\u6848\u683c\u5f0f\u5c0e\u5165\uff0f\u532f\u51fa", "pad.toolbar.timeslider.title": "\u6642\u9593\u8ef8", - "pad.toolbar.savedRevision.title": "\u5df2\u5132\u5b58\u7684\u4fee\u8a02", + "pad.toolbar.savedRevision.title": "\u5132\u5b58\u4fee\u8a02", "pad.toolbar.settings.title": "\u8a2d\u5b9a", "pad.toolbar.embed.title": "\u5d4c\u5165\u6b64pad", "pad.toolbar.showusers.title": "\u986f\u793a\u6b64pad\u7684\u7528\u6236", diff --git a/src/node/db/PadManager.js b/src/node/db/PadManager.js index 5e0af4643..7d546fc71 100644 --- a/src/node/db/PadManager.js +++ b/src/node/db/PadManager.js @@ -146,12 +146,11 @@ exports.getPad = function(id, text, callback) else { pad = new Pad(id); - + //initalize the pad pad.init(text, function(err) { if(ERR(err, callback)) return; - globalPads.set(id, pad); callback(null, pad); }); diff --git a/src/node/handler/ExportHandler.js b/src/node/handler/ExportHandler.js index 1b7fcc26d..8ff5bc488 100644 --- a/src/node/handler/ExportHandler.js +++ b/src/node/handler/ExportHandler.js @@ -20,6 +20,7 @@ var ERR = require("async-stacktrace"); var exporthtml = require("../utils/ExportHtml"); +var exporttxt = require("../utils/ExportTxt"); var exportdokuwiki = require("../utils/ExportDokuWiki"); var padManager = require("../db/PadManager"); var async = require("async"); @@ -48,22 +49,75 @@ exports.doExport = function(req, res, padId, type) res.attachment(padId + "." + type); //if this is a plain text export, we can do this directly + // We have to over engineer this because tabs are stored as attributes and not plain text + if(type == "txt") { - padManager.getPad(padId, function(err, pad) - { - ERR(err); - if(req.params.rev){ - pad.getInternalRevisionAText(req.params.rev, function(junk, text) - { - res.send(text.text ? text.text : null); - }); - } - else + var txt; + var randNum; + var srcFile, destFile; + + async.series([ + //render the txt document + function(callback) { - res.send(pad.text()); + 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 + "/eplite_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) + { + if(err && err != "stop") ERR(err); + }) } else if(type == 'dokuwiki') { diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index eaf24b7a2..7ffeb6e5a 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -1035,7 +1035,7 @@ function handleClientReady(client, message) } // notify all existing users about new user - client.broadcast.to(padIds.padIds).json.send(messageToTheOtherUsers); + client.broadcast.to(padIds.padId).json.send(messageToTheOtherUsers); //Run trough all sessions of this pad async.forEach(socketio.sockets.clients(padIds.padId), function(roomClient, callback) diff --git a/src/node/utils/ExportHelper.js b/src/node/utils/ExportHelper.js new file mode 100644 index 000000000..a939a8b6e --- /dev/null +++ b/src/node/utils/ExportHelper.js @@ -0,0 +1,87 @@ +/** + * Helpers for export requests + */ + +/* + * 2011 Peter 'Pita' Martischka (Primary Technology Ltd) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS-IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var async = require("async"); +var Changeset = require("ep_etherpad-lite/static/js/Changeset"); +var padManager = require("../db/PadManager"); +var ERR = require("async-stacktrace"); +var Security = require('ep_etherpad-lite/static/js/security'); +var hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks'); + +exports.getPadPlainText = function(pad, revNum){ + var atext = ((revNum !== undefined) ? pad.getInternalRevisionAText(revNum) : pad.atext()); + var textLines = atext.text.slice(0, -1).split('\n'); + var attribLines = Changeset.splitAttributionLines(atext.attribs, atext.text); + var apool = pad.pool(); + + var pieces = []; + for (var i = 0; i < textLines.length; i++){ + var line = _analyzeLine(textLines[i], attribLines[i], apool); + if (line.listLevel){ + var numSpaces = line.listLevel * 2 - 1; + var bullet = '*'; + pieces.push(new Array(numSpaces + 1).join(' '), bullet, ' ', line.text, '\n'); + } + else{ + pieces.push(line.text, '\n'); + } + } + + return pieces.join(''); +} + + +exports._analyzeLine = function(text, aline, apool){ + var line = {}; + + // identify list + var lineMarker = 0; + line.listLevel = 0; + if (aline){ + var opIter = Changeset.opIterator(aline); + if (opIter.hasNext()){ + var listType = Changeset.opAttributeValue(opIter.next(), 'list', apool); + if (listType){ + lineMarker = 1; + listType = /([a-z]+)([12345678])/.exec(listType); + if (listType){ + line.listTypeName = listType[1]; + line.listLevel = Number(listType[2]); + } + } + } + } + if (lineMarker){ + line.text = text.substring(1); + line.aline = Changeset.subattribution(aline, 1); + } + else{ + line.text = text; + line.aline = aline; + } + return line; +} + + +exports._encodeWhitespace = function(s){ + return s.replace(/[^\x21-\x7E\s\t\n\r]/g, function(c){ + return "&#" +c.charCodeAt(0) + ";" + }); +} diff --git a/src/node/utils/ExportHtml.js b/src/node/utils/ExportHtml.js index 069194880..585694d4b 100644 --- a/src/node/utils/ExportHtml.js +++ b/src/node/utils/ExportHtml.js @@ -21,31 +21,9 @@ var padManager = require("../db/PadManager"); var ERR = require("async-stacktrace"); var Security = require('ep_etherpad-lite/static/js/security'); var hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks'); -function getPadPlainText(pad, revNum) -{ - var atext = ((revNum !== undefined) ? pad.getInternalRevisionAText(revNum) : pad.atext()); - var textLines = atext.text.slice(0, -1).split('\n'); - var attribLines = Changeset.splitAttributionLines(atext.attribs, atext.text); - var apool = pad.pool(); - - var pieces = []; - for (var i = 0; i < textLines.length; i++) - { - var line = _analyzeLine(textLines[i], attribLines[i], apool); - if (line.listLevel) - { - var numSpaces = line.listLevel * 2 - 1; - var bullet = '*'; - pieces.push(new Array(numSpaces + 1).join(' '), bullet, ' ', line.text, '\n'); - } - else - { - pieces.push(line.text, '\n'); - } - } - - return pieces.join(''); -} +var getPadPlainText = require('./ExportHelper').getPadPlainText +var _analyzeLine = require('./ExportHelper')._analyzeLine; +var _encodeWhitespace = require('./ExportHelper')._encodeWhitespace; function getPadHTML(pad, revNum, callback) { @@ -503,45 +481,6 @@ function getHTMLFromAtext(pad, atext, authorColors) return pieces.join(''); } -function _analyzeLine(text, aline, apool) -{ - var line = {}; - - // identify list - var lineMarker = 0; - line.listLevel = 0; - if (aline) - { - var opIter = Changeset.opIterator(aline); - if (opIter.hasNext()) - { - var listType = Changeset.opAttributeValue(opIter.next(), 'list', apool); - if (listType) - { - lineMarker = 1; - listType = /([a-z]+)([12345678])/.exec(listType); - if (listType) - { - line.listTypeName = listType[1]; - line.listLevel = Number(listType[2]); - } - } - } - } - if (lineMarker) - { - line.text = text.substring(1); - line.aline = Changeset.subattribution(aline, 1); - } - else - { - line.text = text; - line.aline = aline; - } - - return line; -} - exports.getPadHTMLDocument = function (padId, revNum, noDocType, callback) { padManager.getPad(padId, function (err, pad) @@ -578,79 +517,6 @@ exports.getPadHTMLDocument = function (padId, revNum, noDocType, callback) }); } -function _encodeWhitespace(s) { - return s.replace(/[^\x21-\x7E\s\t\n\r]/g, function(c) - { - return "&#" +c.charCodeAt(0) + ";" - }); -} - -// copied from ACE - - -function _processSpaces(s) -{ - var doesWrap = true; - if (s.indexOf("<") < 0 && !doesWrap) - { - // short-cut - return s.replace(/ /g, ' '); - } - var parts = []; - s.replace(/<[^>]*>?| |[^ <]+/g, function (m) - { - parts.push(m); - }); - if (doesWrap) - { - var endOfLine = true; - var beforeSpace = false; - // last space in a run is normal, others are nbsp, - // end of line is nbsp - for (var i = parts.length - 1; i >= 0; i--) - { - var p = parts[i]; - if (p == " ") - { - if (endOfLine || beforeSpace) parts[i] = ' '; - endOfLine = false; - beforeSpace = true; - } - else if (p.charAt(0) != "<") - { - endOfLine = false; - beforeSpace = false; - } - } - // beginning of line is nbsp - for (var i = 0; i < parts.length; i++) - { - var p = parts[i]; - if (p == " ") - { - parts[i] = ' '; - break; - } - else if (p.charAt(0) != "<") - { - break; - } - } - } - else - { - for (var i = 0; i < parts.length; i++) - { - var p = parts[i]; - if (p == " ") - { - parts[i] = ' '; - } - } - } - return parts.join(''); -} - // copied from ACE var _REGEX_WORDCHAR = /[\u0030-\u0039\u0041-\u005A\u0061-\u007A\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\u1FFF\u3040-\u9FFF\uF900-\uFDFF\uFE70-\uFEFE\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFDC]/; @@ -676,3 +542,57 @@ function _findURLs(text) return urls; } + + +// copied from ACE +function _processSpaces(s){ + var doesWrap = true; + if (s.indexOf("<") < 0 && !doesWrap){ + // short-cut + return s.replace(/ /g, ' '); + } + var parts = []; + s.replace(/<[^>]*>?| |[^ <]+/g, function (m){ + parts.push(m); + }); + if (doesWrap){ + var endOfLine = true; + var beforeSpace = false; + // last space in a run is normal, others are nbsp, + // end of line is nbsp + for (var i = parts.length - 1; i >= 0; i--){ + var p = parts[i]; + if (p == " "){ + if (endOfLine || beforeSpace) parts[i] = ' '; + endOfLine = false; + beforeSpace = true; + } + else if (p.charAt(0) != "<"){ + endOfLine = false; + beforeSpace = false; + } + } + // beginning of line is nbsp + for (var i = 0; i < parts.length; i++){ + var p = parts[i]; + if (p == " "){ + parts[i] = ' '; + break; + } + else if (p.charAt(0) != "<"){ + break; + } + } + } + else + { + for (var i = 0; i < parts.length; i++){ + var p = parts[i]; + if (p == " "){ + parts[i] = ' '; + } + } + } + return parts.join(''); +} + diff --git a/src/node/utils/ExportTxt.js b/src/node/utils/ExportTxt.js new file mode 100644 index 000000000..c57424f1d --- /dev/null +++ b/src/node/utils/ExportTxt.js @@ -0,0 +1,293 @@ +/** + * TXT export + */ + +/* + * 2013 John McLear + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS-IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var async = require("async"); +var Changeset = require("ep_etherpad-lite/static/js/Changeset"); +var padManager = require("../db/PadManager"); +var ERR = require("async-stacktrace"); +var Security = require('ep_etherpad-lite/static/js/security'); +var hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks'); +var getPadPlainText = require('./ExportHelper').getPadPlainText; +var _analyzeLine = require('./ExportHelper')._analyzeLine; + +// This is slightly different than the HTML method as it passes the output to getTXTFromAText +function getPadTXT(pad, revNum, callback) +{ + var atext = pad.atext; + var html; + async.waterfall([ + // fetch revision atext + + + function (callback) + { + if (revNum != undefined) + { + pad.getInternalRevisionAText(revNum, function (err, revisionAtext) + { + if(ERR(err, callback)) return; + atext = revisionAtext; + callback(); + }); + } + else + { + callback(null); + } + }, + + // convert atext to html + + + function (callback) + { + html = getTXTFromAtext(pad, atext); // only this line is different to the HTML function + callback(null); + }], + // run final callback + + + function (err) + { + if(ERR(err, callback)) return; + callback(null, html); + }); +} + +exports.getPadTXT = getPadTXT; + + +// This is different than the functionality provided in ExportHtml as it provides formatting +// functionality that is designed specifically for TXT exports +function getTXTFromAtext(pad, atext, authorColors) +{ + var apool = pad.apool(); + var textLines = atext.text.slice(0, -1).split('\n'); + var attribLines = Changeset.splitAttributionLines(atext.attribs, atext.text); + + var tags = ['h1', 'h2', 'strong', 'em', 'u', 's']; + var props = ['heading1', 'heading2', 'bold', 'italic', 'underline', 'strikethrough']; + var anumMap = {}; + var css = ""; + + props.forEach(function (propName, i) + { + var propTrueNum = apool.putAttrib([propName, true], true); + if (propTrueNum >= 0) + { + anumMap[propTrueNum] = i; + } + }); + + function getLineTXT(text, attribs) + { + var propVals = [false, false, false]; + var ENTER = 1; + var STAY = 2; + var LEAVE = 0; + + // Use order of tags (b/i/u) as order of nesting, for simplicity + // and decent nesting. For example, + // Just bold Bold and italics Just italics + // becomes + // Just bold Bold and italics Just italics + var taker = Changeset.stringIterator(text); + var assem = Changeset.stringAssembler(); + var openTags = []; + + var idx = 0; + + function processNextChars(numChars) + { + if (numChars <= 0) + { + return; + } + + var iter = Changeset.opIterator(Changeset.subattribution(attribs, idx, idx + numChars)); + idx += numChars; + + while (iter.hasNext()) + { + var o = iter.next(); + var propChanged = false; + Changeset.eachAttribNumber(o.attribs, function (a) + { + if (a in anumMap) + { + var i = anumMap[a]; // i = 0 => bold, etc. + if (!propVals[i]) + { + propVals[i] = ENTER; + propChanged = true; + } + else + { + propVals[i] = STAY; + } + } + }); + for (var i = 0; i < propVals.length; i++) + { + if (propVals[i] === true) + { + propVals[i] = LEAVE; + propChanged = true; + } + else if (propVals[i] === STAY) + { + propVals[i] = true; // set it back + } + } + // now each member of propVal is in {false,LEAVE,ENTER,true} + // according to what happens at start of span + if (propChanged) + { + // leaving bold (e.g.) also leaves italics, etc. + var left = false; + for (var i = 0; i < propVals.length; i++) + { + var v = propVals[i]; + if (!left) + { + if (v === LEAVE) + { + left = true; + } + } + else + { + if (v === true) + { + propVals[i] = STAY; // tag will be closed and re-opened + } + } + } + + var tags2close = []; + + for (var i = propVals.length - 1; i >= 0; i--) + { + if (propVals[i] === LEAVE) + { + //emitCloseTag(i); + tags2close.push(i); + propVals[i] = false; + } + else if (propVals[i] === STAY) + { + //emitCloseTag(i); + tags2close.push(i); + } + } + + for (var i = 0; i < propVals.length; i++) + { + if (propVals[i] === ENTER || propVals[i] === STAY) + { + propVals[i] = true; + } + } + // propVals is now all {true,false} again + } // end if (propChanged) + + var chars = o.chars; + if (o.lines) + { + chars--; // exclude newline at end of line, if present + } + + var s = taker.take(chars); + + // 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), ""); + + // remove * from s, it's just not needed on a blank line.. This stops + // plugins from being able to display * at the beginning of a line + // s = s.replace("*", ""); // Then remove it + + assem.append(s); + } // end iteration over spans in line + + var tags2close = []; + for (var i = propVals.length - 1; i >= 0; i--) + { + if (propVals[i]) + { + tags2close.push(i); + propVals[i] = false; + } + } + + } // end processNextChars + processNextChars(text.length - idx); + return(assem.toString()); + } // end getLineHTML + var pieces = [css]; + + // Need to deal with constraints imposed on HTML lists; can + // only gain one level of nesting at once, can't change type + // mid-list, etc. + // People might use weird indenting, e.g. skip a level, + // so we want to do something reasonable there. We also + // want to deal gracefully with blank lines. + // => keeps track of the parents level of indentation + var lists = []; // e.g. [[1,'bullet'], [3,'bullet'], ...] + for (var i = 0; i < textLines.length; i++) + { + var line = _analyzeLine(textLines[i], attribLines[i], apool); + var lineContent = getLineTXT(line.text, line.aline); + if(line.listTypeName == "bullet"){ + lineContent = "* " + lineContent; // add a bullet + } + if(line.listLevel > 0){ + for (var j = line.listLevel - 1; j >= 0; j--){ + pieces.push('\t'); + } + if(line.listTypeName == "number"){ + pieces.push(line.listLevel + ". "); + // This is bad because it doesn't truly reflect what the user + // sees because browsers do magic on nested
  1. s + } + pieces.push(lineContent, '\n'); + }else{ + pieces.push(lineContent, '\n'); + } + } + + return pieces.join(''); +} +exports.getTXTFromAtext = getTXTFromAtext; + +exports.getPadTXTDocument = function (padId, revNum, noDocType, callback) +{ + padManager.getPad(padId, function (err, pad) + { + if(ERR(err, callback)) return; + + getPadTXT(pad, revNum, function (err, html) + { + if(ERR(err, callback)) return; + callback(null, html); + }); + }); +} + diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 70b5443bd..ddb614bec 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -1622,9 +1622,17 @@ function Ace2Inner(){ lines = ccData.lines; var lineAttribs = ccData.lineAttribs; var linesWrapped = ccData.linesWrapped; + var scrollToTheLeftNeeded = false; if (linesWrapped > 0) { + if(!browser.ie){ + // chrome decides in it's infinite wisdom that its okay to put the browsers visisble window in the middle of the span + // an outcome of this is that the first chars of the string are no longer visible to the user.. Yay chrome.. + // Move the browsers visible area to the left hand side of the span + // Firefox isn't quite so bad, but it's still pretty quirky. + var scrollToTheLeftNeeded = true; + } // console.log("Editor warning: " + linesWrapped + " long line" + (linesWrapped == 1 ? " was" : "s were") + " hard-wrapped into " + ccData.numLinesAfter + " lines."); } @@ -1692,6 +1700,10 @@ function Ace2Inner(){ //console.log("removed: "+id); }); + if(scrollToTheLeftNeeded){ // needed to stop chrome from breaking the ui when long strings without spaces are pasted + $("#innerdocbody").scrollLeft(0); + } + p.mark("findsel"); // if the nodes that define the selection weren't encountered during // content collection, figure out where those nodes are now. @@ -1897,7 +1909,7 @@ function Ace2Inner(){ var prevLine = rep.lines.prev(thisLine); var prevLineText = prevLine.text; var theIndent = /^ *(?:)/.exec(prevLineText)[0]; - if (/[\[\(\{]\s*$/.exec(prevLineText)) theIndent += THE_TAB; + if (/[\[\(\:\{]\s*$/.exec(prevLineText)) theIndent += THE_TAB; var cs = Changeset.builder(rep.lines.totalWidth()).keep( rep.lines.offsetOfIndex(lineNum), lineNum).insert( theIndent, [ @@ -3287,7 +3299,7 @@ function Ace2Inner(){ listType = /([a-z]+)([12345678])/.exec(listType); var type = listType[1]; var level = Number(listType[2]); - + //detect empty list item; exclude indentation if(text === '*' && type !== "indent") { @@ -3317,8 +3329,10 @@ function Ace2Inner(){ function doIndentOutdent(isOut) { - if (!(rep.selStart && rep.selEnd) || - ((rep.selStart[0] == rep.selEnd[0]) && (rep.selStart[1] == rep.selEnd[1]) && rep.selEnd[1] > 1)) + if (!((rep.selStart && rep.selEnd) || + ((rep.selStart[0] == rep.selEnd[0]) && (rep.selStart[1] == rep.selEnd[1]) && rep.selEnd[1] > 1)) && + (isOut != true) + ) { return false; } @@ -3326,7 +3340,6 @@ function Ace2Inner(){ var firstLine, lastLine; firstLine = rep.selStart[0]; lastLine = Math.max(firstLine, rep.selEnd[0] - ((rep.selEnd[1] === 0) ? 1 : 0)); - var mods = []; for (var n = firstLine; n <= lastLine; n++) { @@ -3539,7 +3552,6 @@ function Ace2Inner(){ { // if (DEBUG && window.DONT_INCORP) return; if (!isEditable) return; - var type = evt.type; var charCode = evt.charCode; var keyCode = evt.keyCode; @@ -3736,26 +3748,30 @@ function Ace2Inner(){ We have to do this the way we do because rep. doesn't hold the value for keyheld events IE if the user presses and holds the arrow key */ if((evt.which == 37 || evt.which == 38 || evt.which == 39 || evt.which == 40) && $.browser.chrome){ - var isLeftArrow = evt.which === 37; - var isUpArrow = evt.which === 38; - var isRightArrow = evt.which === 39; - var isDownArrow = evt.which === 40; var newVisibleLineRange = getVisibleLineRange(); // get the current visible range -- This works great. var lineHeight = textLineHeight(); // what Is the height of each line? var myselection = document.getSelection(); // get the current caret selection, can't use rep. here because that only gives us the start position not the current var caretOffsetTop = myselection.focusNode.parentNode.offsetTop; // get the carets selection offset in px IE 214 - if((isUpArrow || isLeftArrow || isRightArrow || isDownArrow) && caretOffsetTop){ // was it an up arrow or left arrow? + if(caretOffsetTop){ // sometimes caretOffsetTop bugs out and returns 0, not sure why, possible Chrome bug? Either way if it does we don't wanna mess with it var lineNum = Math.round(caretOffsetTop / lineHeight) ; // Get the current Line Number IE 84 + newVisibleLineRange[1] = newVisibleLineRange[1]-1; var caretIsVisible = (lineNum > newVisibleLineRange[0] && lineNum < newVisibleLineRange[1]); // Is the cursor in the visible Range IE ie 84 > 14 and 84 < 90? + if(!caretIsVisible){ // is the cursor no longer visible to the user? // Oh boy the caret is out of the visible area, I need to scroll the browser window to lineNum. // Get the new Y by getting the line number and multiplying by the height of each line. - var newY = lineHeight * (lineNum -1); // -1 to go to the line above + if(evt.which == 37 || evt.which == 38){ // If left or up + var newY = lineHeight * (lineNum -1); // -1 to go to the line above + }else if(evt.which == 39 || evt.which == 40){ // if down or right + var newY = getScrollY() + (lineHeight*3); // the offset and one additional line + } setScrollY(newY); // set the scroll height of the browser } + } + } } diff --git a/src/static/js/l10n.js b/src/static/js/l10n.js index a67a7c1a4..c79ea706d 100644 --- a/src/static/js/l10n.js +++ b/src/static/js/l10n.js @@ -1,8 +1,8 @@ (function(document) { // Set language for l10n - var language = document.cookie.match(/language=((\w{2,3})(-w+)?)/); + var language = document.cookie.match(/language=((\w{2,3})(-\w+)?)/); if(language) language = language[1]; - + html10n.bind('indexed', function() { html10n.localize([language, navigator.language, navigator.userLanguage, 'en']) }) diff --git a/src/static/js/pad_editbar.js b/src/static/js/pad_editbar.js index 91a07bf96..cc9f8758c 100644 --- a/src/static/js/pad_editbar.js +++ b/src/static/js/pad_editbar.js @@ -156,10 +156,7 @@ var padeditbar = (function() else if (cmd == 'insertorderedlist') ace.ace_doInsertOrderedList(); else if (cmd == 'indent') { - if (!ace.ace_doIndentOutdent(false)) - { - ace.ace_doInsertUnorderedList(); - } + ace.ace_doIndentOutdent(false); } else if (cmd == 'outdent') { diff --git a/src/static/js/pluginfw/hooks.js b/src/static/js/pluginfw/hooks.js index d9a14d857..4d5057949 100644 --- a/src/static/js/pluginfw/hooks.js +++ b/src/static/js/pluginfw/hooks.js @@ -70,10 +70,12 @@ exports.flatten = function (lst) { exports.callAll = function (hook_name, args) { if (!args) args = {}; - if (exports.plugins.hooks[hook_name] === undefined) return []; - return _.flatten(_.map(exports.plugins.hooks[hook_name], function (hook) { - return hookCallWrapper(hook, hook_name, args); - }), true); + if (exports.plugins){ + if (exports.plugins.hooks[hook_name] === undefined) return []; + return _.flatten(_.map(exports.plugins.hooks[hook_name], function (hook) { + return hookCallWrapper(hook, hook_name, args); + }), true); + } } exports.aCallAll = function (hook_name, args, cb) { diff --git a/src/static/js/timeslider.js b/src/static/js/timeslider.js index 24e6e98de..eb3703d9f 100644 --- a/src/static/js/timeslider.js +++ b/src/static/js/timeslider.js @@ -29,6 +29,7 @@ var createCookie = require('./pad_utils').createCookie; var readCookie = require('./pad_utils').readCookie; var randomString = require('./pad_utils').randomString; var _ = require('./underscore'); +var hooks = require('./pluginfw/hooks'); var token, padId, export_links; @@ -108,6 +109,7 @@ function init() { exports.socket = socket; // make the socket available + hooks.aCallAll("postTimesliderInit"); }); } diff --git a/src/templates/timeslider.html b/src/templates/timeslider.html index 5d0e357c6..d3062449f 100644 --- a/src/templates/timeslider.html +++ b/src/templates/timeslider.html @@ -40,8 +40,10 @@ <% e.end_block(); %> + <% e.begin_block("timesliderScripts"); %> + <% e.end_block(); %> <% e.begin_block("timesliderBody"); %>