Merge branch 'release/1.2.8'

This commit is contained in:
John McLear 2013-03-05 13:39:53 +00:00
commit cb2d148734
95 changed files with 7555 additions and 6982 deletions

View file

@ -1,3 +1,39 @@
# 1.2.8
! IMPORTANT: New setting.json value is required to automatically reconnect clients on disconnect
* NEW: Use Socket IO for rooms (allows for pads to be load balanced with sticky rooms)
* 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
* NEW: CLI script to delete pads
* Fix: Automatic client reconnection on disonnect
* 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: Stop the client disconnecting if a rev is in the wrong order
* Fix: Various server crash issues based on rev in wrong order
* Fix: Various tests
* 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: Redis findKeys support
* 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
* Fix: Page up / Page down now works in all browsers
* Fix: Stop Opera browser inserting two new lines on enter keypress
* Fix: Stop timeslider from showing NaN on pads with only one revision
* Other: Allow timeslider tests to run and provide & fix various other frontend-tests
* Other: Begin dropping referene to Lite. Etherpad Lite is now named "Etherpad"
* Other: Update to latest jQuery
* Other: Change loading message asking user to please wait on first build
* Other: Allow etherpad to use global npm installation (Safe since node 6.3)
* Other: Better documentation for log rotation and log message handling
# 1.2.7 # 1.2.7
* NEW: notifications are now modularized and can be stacked * NEW: notifications are now modularized and can be stacked
* NEW: Visit a specific revision in the timeslider by suffixing #%revNumber% IE http://localhost/p/test/timeslider#12 * NEW: Visit a specific revision in the timeslider by suffixing #%revNumber% IE http://localhost/p/test/timeslider#12

View file

@ -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) 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. 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! 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.) 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 # 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)! 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)!
@ -122,6 +122,7 @@ Join the [mailinglist](http://groups.google.com/group/etherpad-lite-dev) and mak
# Donate! # Donate!
* [Flattr] (http://flattr.com/thing/71378/Etherpad-Foundation) * [Flattr] (http://flattr.com/thing/71378/Etherpad-Foundation)
* Paypal - Press the donate button on [etherpad.org](http://etherpad.org) * Paypal - Press the donate button on [etherpad.org](http://etherpad.org)
* [Bitcoin] (https://coinbase.com/checkouts/1e572bf8a82e4663499f7f1f66c2d15a)
# License # License
[Apache License v2](http://www.apache.org/licenses/LICENSE-2.0.html) [Apache License v2](http://www.apache.org/licenses/LICENSE-2.0.html)

63
bin/deletePad.js Normal file
View file

@ -0,0 +1,63 @@
/*
A tool for deleting pads from the CLI, because sometimes a brick is required to fix a window.
*/
if(process.argv.length != 3)
{
console.error("Use: node deletePad.js $PADID");
process.exit(1);
}
//get the padID
var padId = process.argv[2];
var db, padManager, pad, settings;
var neededDBValues = ["pad:"+padId];
var npm = require("../src/node_modules/npm");
var async = require("../src/node_modules/async");
async.series([
// load npm
function(callback) {
npm.load({}, function(er) {
if(er)
{
console.error("Could not load NPM: " + er)
process.exit(1);
}
else
{
callback();
}
})
},
// load modules
function(callback) {
settings = require('../src/node/utils/Settings');
db = require('../src/node/db/DB');
callback();
},
// intallize the database
function (callback)
{
db.init(callback);
},
// delete the pad and it's links
function (callback)
{
padManager = require('../src/node/db/PadManager');
padManager.removePad(padId, function(err){
callback(err);
});
callback();
}
], function (err)
{
if(err) throw err;
else
{
console.log("Finished deleting padId: "+padId);
process.exit();
}
});

View file

@ -13,8 +13,8 @@ var padId = process.argv[2];
var db, dirty, padManager, pad, settings; var db, dirty, padManager, pad, settings;
var neededDBValues = ["pad:"+padId]; var neededDBValues = ["pad:"+padId];
var npm = require("../src/node_modules/npm"); var npm = require("../node_modules/ep_etherpad-lite/node_modules/npm");
var async = require("../src/node_modules/async"); var async = require("../node_modules/ep_etherpad-lite/node_modules/async");
async.series([ async.series([
// load npm // load npm
@ -33,9 +33,10 @@ async.series([
}, },
// load modules // load modules
function(callback) { function(callback) {
settings = require('../src/node/utils/Settings'); settings = require('../node_modules/ep_etherpad-lite/node/utils/Settings');
db = require('../src/node/db/DB'); db = require('../node_modules/ep_etherpad-lite/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 //intallize the database
function (callback) function (callback)
@ -45,7 +46,7 @@ async.series([
//get the pad //get the pad
function (callback) function (callback)
{ {
padManager = require('../node/db/PadManager'); padManager = require('../node_modules/ep_etherpad-lite/node/db/PadManager');
padManager.getPad(padId, function(err, _pad) padManager.getPad(padId, function(err, _pad)
{ {
@ -82,7 +83,10 @@ async.series([
db.db.db.wrappedDB.get(dbkey, function(err, dbvalue) db.db.db.wrappedDB.get(dbkey, function(err, dbvalue)
{ {
if(err) { callback(err); return} if(err) { callback(err); return}
dbvalue=JSON.parse(dbvalue);
if(dbvalue && typeof dbvalue != 'object'){
dbvalue=JSON.parse(dbvalue); // if its not json then parse it as json
}
dirty.set(dbkey, dbvalue, callback); dirty.set(dbkey, dbvalue, callback);
}); });

View file

@ -63,7 +63,7 @@ if [ ! -f $settings ]; then
cp settings.json.template $settings || exit 1 cp settings.json.template $settings || exit 1
fi fi
echo "Ensure that all dependencies are up to date..." echo "Ensure that all dependencies are up to date... If this is the first time you have run Etherpad please be patient."
( (
mkdir -p node_modules mkdir -p node_modules
cd node_modules cd node_modules
@ -77,7 +77,7 @@ echo "Ensure that all dependencies are up to date..."
echo "Ensure jQuery is downloaded and up to date..." echo "Ensure jQuery is downloaded and up to date..."
DOWNLOAD_JQUERY="true" DOWNLOAD_JQUERY="true"
NEEDED_VERSION="1.7.1" NEEDED_VERSION="1.9.1"
if [ -f "src/static/js/jquery.js" ]; then if [ -f "src/static/js/jquery.js" ]; then
if [ $(uname) = "SunOS" ]; then if [ $(uname) = "SunOS" ]; then
VERSION=$(cat src/static/js/jquery.js | head -n 3 | ggrep -o "v[0-9]\.[0-9]\(\.[0-9]\)\?"); VERSION=$(cat src/static/js/jquery.js | head -n 3 | ggrep -o "v[0-9]\.[0-9]\(\.[0-9]\)\?");

View file

@ -12,7 +12,7 @@ set check_version="if(['6','8'].indexOf(process.version.split('.')[1].toString()
cmd /C node -e %check_version% || exit /B 1 cmd /C node -e %check_version% || exit /B 1
echo _ echo _
echo Installing etherpad-lite and dependencies... echo Ensure that all dependencies are up to date... If this is the first time you have run Etherpad please be patient.
cmd /C npm install src/ --loglevel warn || exit /B 1 cmd /C npm install src/ --loglevel warn || exit /B 1
echo _ echo _
@ -36,4 +36,4 @@ IF NOT EXIST settings.json (
) )
echo _ echo _
echo Installed Etherpad-lite! To run Etherpad type start.bat echo Installed Etherpad! To run Etherpad type start.bat

View file

@ -1,3 +1,7 @@
This is the new load testing file: https://bitbucket.org/rbraakman/etherpad-stresstest
BELOW is the original load testing file.
This load tester is extremely useful for testing how many dormant clients can connect to etherpad lite. This load tester is extremely useful for testing how many dormant clients can connect to etherpad lite.
TODO: TODO:

View file

@ -129,6 +129,11 @@ Things in context:
There doesn't appear to be any example available of this particular hook being used, but it gets fired after the editor is all set up. There doesn't appear to be any example available of this particular hook being used, but it gets fired after the editor is all set up.
## postTimesliderInit
Called from: src/static/js/timeslider.js
There doesn't appear to be any example available of this particular hook being used, but it gets fired after the timeslider is all set up.
## userJoinOrUpdate ## userJoinOrUpdate
Called from: src/static/js/pad_userlist.js Called from: src/static/js/pad_userlist.js

View file

@ -61,7 +61,9 @@ Portal submits content into new blog post
## Usage ## Usage
### API version ### API version
The latest version is `1.2` The latest version is `1.2.7`
The current version can be queried via /api.
### Request Format ### Request Format

View file

@ -105,3 +105,15 @@ Your plugin must also contain a [package definition file](http://npmjs.org/doc/j
## Templates ## Templates
If your plugin adds or modifies the front end HTML (e.g. adding buttons or changing their functions), you should put the necessary HTML code for such operations in `templates/`, in files of type ".ejs", since Etherpad-Lite uses EJS for HTML templating. See the following link for more information about EJS: <https://github.com/visionmedia/ejs>. If your plugin adds or modifies the front end HTML (e.g. adding buttons or changing their functions), you should put the necessary HTML code for such operations in `templates/`, in files of type ".ejs", since Etherpad-Lite uses EJS for HTML templating. See the following link for more information about EJS: <https://github.com/visionmedia/ejs>.
## Writing and running front-end tests for your plugin
Etherpad allows you to easily create front-end tests for plugins.
1. Create a new folder
```
%your_plugin%/static/tests/frontend/specs
```
2. Put your spec file in here (Example spec files are visible in %etherpad_root_folder%/frontend/tests/specs)
3. Visit http://yourserver.com/frontend/tests your front-end tests will run.

View file

@ -5,7 +5,7 @@
*/ */
{ {
// Name your instance! // Name your instance!
"title": "Etherpad Lite", "title": "Etherpad",
// favicon default name // favicon default name
// alternatively, set up a fully specified Url to your own favicon // alternatively, set up a fully specified Url to your own favicon
@ -15,6 +15,10 @@
"ip": "0.0.0.0", "ip": "0.0.0.0",
"port" : 9001, "port" : 9001,
// Session Key, used for reconnecting user sessions
// Set this to a secure string at least 10 characters long. Do not share this value.
"sessionKey" : "",
/* /*
// Node native SSL support // Node native SSL support
// this is disabled by default // this is disabled by default
@ -47,15 +51,8 @@
}, },
*/ */
//Logging configuration. See log4js documentation for further information
// https://github.com/nomiddlename/log4js-node
"logconfig" :
{ "appenders": [
{ "type": "console" }
] },
//the default text of a pad //the default text of a pad
"defaultPadText" : "Welcome to Etherpad Lite!\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\nGet involved with Etherpad at http:\/\/etherpad.org\n", "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\nGet involved with Etherpad at http:\/\/etherpad.org\n",
/* Users must have a session to access pads. This effectively allows only group pads to be accessed. */ /* Users must have a session to access pads. This effectively allows only group pads to be accessed. */
"requireSession" : false, "requireSession" : false,
@ -97,9 +94,50 @@
}, },
*/ */
// restrict socket.io transport methods
"socketTransportProtocols" : ["xhr-polling", "jsonp-polling", "htmlfile"],
/* The log level we are using, can be: DEBUG, INFO, WARN, ERROR */ /* The log level we are using, can be: DEBUG, INFO, WARN, ERROR */
"loglevel": "INFO", "loglevel": "INFO",
// restrict socket.io transport methods //Logging configuration. See log4js documentation for further information
"socketTransportProtocols" : ["xhr-polling", "jsonp-polling", "htmlfile"] // https://github.com/nomiddlename/log4js-node
// You can add as many appenders as you want here:
"logconfig" :
{ "appenders": [
{ "type": "console"
//, "category": "access"// only logs pad access
}
/*
, { "type": "file"
, "filename": "your-log-file-here.log"
, "maxLogSize": 1024
, "backups": 3 // how many log files there're gonna be at max
//, "category": "test" // only log a specific category
}*/
/*
, { "type": "logLevelFilter"
, "level": "warn" // filters out all log messages that have a lower level than "error"
, "appender":
{ Use whatever appender you want here }
}*/
/*
, { "type": "logLevelFilter"
, "level": "error" // filters out all log messages that have a lower level than "error"
, "appender":
{ "type": "smtp"
, "subject": "An error occured in your EPL instance!"
, "recipients": "bar@blurdybloop.com, baz@blurdybloop.com"
, "sendInterval": 60*5 // in secs -- will buffer log messages; set to 0 to send a mail for every message
, "transport": "SMTP", "SMTP": { // see https://github.com/andris9/Nodemailer#possible-transport-methods
"host": "smtp.example.com", "port": 465,
"secureConnection": true,
"auth": {
"user": "foo@example.com",
"pass": "bar_foo"
}
}
}
}*/
] }
} }

1
src/README.md Normal file
View file

@ -0,0 +1 @@
Ignore this file and see the file in the base installation folder

View file

@ -2,26 +2,60 @@
"@metadata": { "@metadata": {
"authors": [ "authors": [
"Bellayet", "Bellayet",
"Nasir8891" "Nasir8891",
"Sankarshan"
] ]
}, },
"index.newPad": "\u09a8\u09a4\u09c1\u09a8 \u09aa\u09cd\u09af\u09be\u09a1", "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:", "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.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.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.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.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.save": "\u09b8\u0982\u09b0\u0995\u09cd\u09b7\u09a3",
"pad.colorpicker.cancel": "\u09ac\u09be\u09a4\u09bf\u09b2", "pad.colorpicker.cancel": "\u09ac\u09be\u09a4\u09bf\u09b2",
"pad.loading": "\u09b2\u09cb\u09a1\u09bf\u0982...", "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.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.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.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.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.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.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.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.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": "\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.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", "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.authors": "\u09b2\u09c7\u0996\u0995\u0997\u09a3:",
"timeslider.toolbar.authorsList": "\u0995\u09cb\u09a8\u09cb \u09b2\u09c7\u0996\u0995 \u09a8\u09c7\u0987", "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.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.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.february": "\u09ab\u09c7\u09ac\u09cd\u09b0\u09c1\u09af\u09bc\u09be\u09b0\u09bf",
"timeslider.month.march": "\u09ae\u09be\u09b0\u09cd\u099a", "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.september": "\u09b8\u09c7\u09aa\u09cd\u099f\u09c7\u09ae\u09cd\u09ac\u09b0",
"timeslider.month.october": "\u0985\u0995\u09cd\u099f\u09cb\u09ac\u09b0", "timeslider.month.october": "\u0985\u0995\u09cd\u099f\u09cb\u09ac\u09b0",
"timeslider.month.november": "\u09a8\u09ad\u09c7\u09ae\u09cd\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"
} }

123
src/locales/br.json Normal file
View file

@ -0,0 +1,123 @@
{
"@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 <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> 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",
"timeslider.month.april": "Ebrel",
"timeslider.month.may": "Mae",
"timeslider.month.june": "Mezheven",
"timeslider.month.july": "Gouere",
"timeslider.month.august": "Eost",
"timeslider.month.september": "Gwengolo",
"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",
"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."
}

View file

@ -17,7 +17,7 @@
"pad.toolbar.undo.title": "Desf\u00e9s (Ctrl-Z)", "pad.toolbar.undo.title": "Desf\u00e9s (Ctrl-Z)",
"pad.toolbar.redo.title": "Ref\u00e9s (Ctrl-Y)", "pad.toolbar.redo.title": "Ref\u00e9s (Ctrl-Y)",
"pad.toolbar.clearAuthorship.title": "Neteja els colors d'autoria", "pad.toolbar.clearAuthorship.title": "Neteja els colors d'autoria",
"pad.toolbar.savedRevision.title": "Revisions desades", "pad.toolbar.savedRevision.title": "Desa la revisi\u00f3",
"pad.toolbar.settings.title": "Configuraci\u00f3", "pad.toolbar.settings.title": "Configuraci\u00f3",
"pad.toolbar.showusers.title": "Mostra els usuaris d\u2019aquest pad", "pad.toolbar.showusers.title": "Mostra els usuaris d\u2019aquest pad",
"pad.colorpicker.save": "Desa", "pad.colorpicker.save": "Desa",
@ -25,18 +25,25 @@
"pad.loading": "S'est\u00e0 carregant...", "pad.loading": "S'est\u00e0 carregant...",
"pad.wrongPassword": "La contrasenya \u00e9s incorrecta", "pad.wrongPassword": "La contrasenya \u00e9s incorrecta",
"pad.settings.myView": "La meva vista", "pad.settings.myView": "La meva vista",
"pad.settings.stickychat": "Xateja sempre a la pantalla",
"pad.settings.colorcheck": "Colors d'autoria",
"pad.settings.linenocheck": "N\u00fameros de l\u00ednia", "pad.settings.linenocheck": "N\u00fameros de l\u00ednia",
"pad.settings.fontType": "Tipus de lletra:", "pad.settings.fontType": "Tipus de lletra:",
"pad.settings.fontType.normal": "Normal", "pad.settings.fontType.normal": "Normal",
"pad.settings.fontType.monospaced": "D'amplada fixa",
"pad.settings.globalView": "Vista global", "pad.settings.globalView": "Vista global",
"pad.settings.language": "Llengua:", "pad.settings.language": "Llengua:",
"pad.importExport.import_export": "Importaci\u00f3\/exportaci\u00f3", "pad.importExport.import_export": "Importaci\u00f3\/exportaci\u00f3",
"pad.importExport.import": "Puja qualsevol fitxer de text o document", "pad.importExport.import": "Puja qualsevol fitxer de text o document",
"pad.importExport.exporthtml": "HTML", "pad.importExport.exporthtml": "HTML",
"pad.importExport.exportplain": "Text net", "pad.importExport.exportplain": "Text net",
"pad.importExport.exportpdf": "PDF",
"pad.importExport.exportopen": "ODF (Open Document Format)",
"pad.importExport.exportdokuwiki": "DokuWiki",
"pad.modals.connected": "Connectat.", "pad.modals.connected": "Connectat.",
"pad.modals.forcereconnect": "For\u00e7a tornar a connectar", "pad.modals.forcereconnect": "For\u00e7a tornar a connectar",
"pad.modals.unauth": "No autoritzat", "pad.modals.unauth": "No autoritzat",
"pad.modals.unauth.explanation": "Els vostres permisos han canviat mentre es visualitzava la p\u00e0gina. Proveu de reconnectar-vos.",
"pad.modals.looping": "Desconnectat.", "pad.modals.looping": "Desconnectat.",
"pad.modals.initsocketfail": "El servidor no \u00e9s accessible.", "pad.modals.initsocketfail": "El servidor no \u00e9s accessible.",
"pad.modals.initsocketfail.explanation": "No s'ha pogut connectar amb el servidor de sincronitzaci\u00f3.", "pad.modals.initsocketfail.explanation": "No s'ha pogut connectar amb el servidor de sincronitzaci\u00f3.",
@ -44,6 +51,7 @@
"pad.modals.slowcommit.explanation": "El servidor no respon.", "pad.modals.slowcommit.explanation": "El servidor no respon.",
"pad.modals.deleted": "Suprimit.", "pad.modals.deleted": "Suprimit.",
"pad.modals.disconnected": "Heu estat desconnectat.", "pad.modals.disconnected": "Heu estat desconnectat.",
"pad.modals.disconnected.cause": "El servidor sembla que no est\u00e0 disponible. Notifiqueu-nos si continua passant.",
"pad.share.readonly": "Nom\u00e9s de lectura", "pad.share.readonly": "Nom\u00e9s de lectura",
"pad.share.link": "Enlla\u00e7", "pad.share.link": "Enlla\u00e7",
"pad.chat": "Xat", "pad.chat": "Xat",
@ -52,6 +60,8 @@
"timeslider.toolbar.exportlink.title": "Exporta", "timeslider.toolbar.exportlink.title": "Exporta",
"timeslider.exportCurrent": "Exporta la versi\u00f3 actual com a:", "timeslider.exportCurrent": "Exporta la versi\u00f3 actual com a:",
"timeslider.version": "Versi\u00f3 {{version}}", "timeslider.version": "Versi\u00f3 {{version}}",
"timeslider.saved": "Desat {{month}} {{day}}, {{year}}",
"timeslider.dateformat": "{{month}}\/{{day}}\/{{year}} {{hours}}:{{minutes}}:{{seconds}}",
"timeslider.month.january": "Gener", "timeslider.month.january": "Gener",
"timeslider.month.february": "Febrer", "timeslider.month.february": "Febrer",
"timeslider.month.march": "Mar\u00e7", "timeslider.month.march": "Mar\u00e7",
@ -69,9 +79,11 @@
"pad.userlist.guest": "Convidat", "pad.userlist.guest": "Convidat",
"pad.userlist.deny": "Refusa", "pad.userlist.deny": "Refusa",
"pad.userlist.approve": "Aprova", "pad.userlist.approve": "Aprova",
"pad.editbar.clearcolors": "Voleu netejar els colors d'autor del document sencer?",
"pad.impexp.importbutton": "Importa ara", "pad.impexp.importbutton": "Importa ara",
"pad.impexp.importing": "Important...", "pad.impexp.importing": "Important...",
"pad.impexp.convertFailed": "No \u00e9s possible d'importar aquest fitxer. Si us plau, podeu provar d'utilitzar un format diferent o copiar i enganxar manualment.", "pad.impexp.convertFailed": "No \u00e9s possible d'importar aquest fitxer. Si us plau, podeu provar d'utilitzar un format diferent o copiar i enganxar manualment.",
"pad.impexp.uploadFailed": "Ha fallat la c\u00e0rrega. Torneu-ho a provar",
"pad.impexp.importfailed": "Ha fallat la importaci\u00f3", "pad.impexp.importfailed": "Ha fallat la importaci\u00f3",
"pad.impexp.copypaste": "Si us plau, copieu i enganxeu" "pad.impexp.copypaste": "Si us plau, copieu i enganxeu"
} }

View file

@ -1,7 +1,8 @@
{ {
"@metadata": { "@metadata": {
"authors": [ "authors": [
"Christian List" "Christian List",
"Peter Alberti"
] ]
}, },
"index.newPad": "Ny Pad", "index.newPad": "Ny Pad",
@ -100,6 +101,8 @@
"timeslider.month.october": "oktober", "timeslider.month.october": "oktober",
"timeslider.month.november": "november", "timeslider.month.november": "november",
"timeslider.month.december": "december", "timeslider.month.december": "december",
"timeslider.unnamedauthor": "{{num}} unavngiven forfatter",
"timeslider.unnamedauthors": "{{num}} unavngivne forfattere",
"pad.savedrevs.marked": "Denne revision er nu markeret som en gemt revision", "pad.savedrevs.marked": "Denne revision er nu markeret som en gemt revision",
"pad.userlist.entername": "Indtast dit navn", "pad.userlist.entername": "Indtast dit navn",
"pad.userlist.unnamed": "ikke-navngivet", "pad.userlist.unnamed": "ikke-navngivet",

View file

@ -22,7 +22,7 @@
"pad.toolbar.clearAuthorship.title": "Autorenfarben zur\u00fccksetzen", "pad.toolbar.clearAuthorship.title": "Autorenfarben zur\u00fccksetzen",
"pad.toolbar.import_export.title": "Import\/Export in verschiedenen Dateiformaten", "pad.toolbar.import_export.title": "Import\/Export in verschiedenen Dateiformaten",
"pad.toolbar.timeslider.title": "Pad-Versionsgeschichte anzeigen", "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.settings.title": "Einstellungen",
"pad.toolbar.embed.title": "Dieses Pad teilen oder einbetten", "pad.toolbar.embed.title": "Dieses Pad teilen oder einbetten",
"pad.toolbar.showusers.title": "Aktuell verbundene Benutzer anzeigen", "pad.toolbar.showusers.title": "Aktuell verbundene Benutzer anzeigen",

View file

@ -22,7 +22,7 @@
"pad.toolbar.clearAuthorship.title": "\u039a\u03b1\u03b8\u03b1\u03c1\u03b9\u03c3\u03bc\u03cc\u03c2 \u03a7\u03c1\u03c9\u03bc\u03ac\u03c4\u03c9\u03bd \u03a3\u03c5\u03bd\u03c4\u03b1\u03ba\u03c4\u03ce\u03bd", "pad.toolbar.clearAuthorship.title": "\u039a\u03b1\u03b8\u03b1\u03c1\u03b9\u03c3\u03bc\u03cc\u03c2 \u03a7\u03c1\u03c9\u03bc\u03ac\u03c4\u03c9\u03bd \u03a3\u03c5\u03bd\u03c4\u03b1\u03ba\u03c4\u03ce\u03bd",
"pad.toolbar.import_export.title": "\u0395\u03b9\u03c3\u03b1\u03b3\u03c9\u03b3\u03ae\/\u0395\u03be\u03b1\u03b3\u03c9\u03b3\u03ae \u03b1\u03c0\u03cc\/\u03c3\u03b5 \u03b4\u03b9\u03b1\u03c6\u03bf\u03c1\u03b5\u03c4\u03b9\u03ba\u03bf\u03cd\u03c2 \u03c4\u03cd\u03c0\u03bf\u03c5\u03c2 \u03b1\u03c1\u03c7\u03b5\u03af\u03c9\u03bd", "pad.toolbar.import_export.title": "\u0395\u03b9\u03c3\u03b1\u03b3\u03c9\u03b3\u03ae\/\u0395\u03be\u03b1\u03b3\u03c9\u03b3\u03ae \u03b1\u03c0\u03cc\/\u03c3\u03b5 \u03b4\u03b9\u03b1\u03c6\u03bf\u03c1\u03b5\u03c4\u03b9\u03ba\u03bf\u03cd\u03c2 \u03c4\u03cd\u03c0\u03bf\u03c5\u03c2 \u03b1\u03c1\u03c7\u03b5\u03af\u03c9\u03bd",
"pad.toolbar.timeslider.title": "\u03a7\u03c1\u03bf\u03bd\u03bf\u03b4\u03b9\u03ac\u03b3\u03c1\u03b1\u03bc\u03bc\u03b1", "pad.toolbar.timeslider.title": "\u03a7\u03c1\u03bf\u03bd\u03bf\u03b4\u03b9\u03ac\u03b3\u03c1\u03b1\u03bc\u03bc\u03b1",
"pad.toolbar.savedRevision.title": "\u0391\u03c0\u03bf\u03b8\u03ae\u03ba\u03b5\u03c5\u03c3\u03b7 \u0388\u03ba\u03b4\u03bf\u03c3\u03b7\u03c2", "pad.toolbar.savedRevision.title": "\u0391\u03c0\u03bf\u03b8\u03b7\u03ba\u03b5\u03c5\u03bc\u03ad\u03bd\u03b5\u03c2 \u0391\u03bd\u03b1\u03b8\u03b5\u03c9\u03c1\u03ae\u03c3\u03b5\u03b9\u03c2",
"pad.toolbar.settings.title": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2", "pad.toolbar.settings.title": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2",
"pad.toolbar.embed.title": "\u0395\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 pad", "pad.toolbar.embed.title": "\u0395\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 pad",
"pad.toolbar.showusers.title": "\u0395\u03bc\u03c6\u03ac\u03bd\u03b9\u03c3\u03b7 \u03c4\u03c9\u03bd \u03c7\u03c1\u03b7\u03c3\u03c4\u03ce\u03bd \u03b1\u03c5\u03c4\u03bf\u03cd \u03c4\u03bf\u03c5 pad", "pad.toolbar.showusers.title": "\u0395\u03bc\u03c6\u03ac\u03bd\u03b9\u03c3\u03b7 \u03c4\u03c9\u03bd \u03c7\u03c1\u03b7\u03c3\u03c4\u03ce\u03bd \u03b1\u03c5\u03c4\u03bf\u03cd \u03c4\u03bf\u03c5 pad",
@ -81,6 +81,7 @@
"pad.share.emebdcode": "URL \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2", "pad.share.emebdcode": "URL \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2",
"pad.chat": "\u03a3\u03c5\u03bd\u03bf\u03bc\u03b9\u03bb\u03af\u03b1", "pad.chat": "\u03a3\u03c5\u03bd\u03bf\u03bc\u03b9\u03bb\u03af\u03b1",
"pad.chat.title": "\u0386\u03bd\u03bf\u03b9\u03b3\u03bc\u03b1 \u03c4\u03b7\u03c2 \u03c3\u03c5\u03bd\u03bf\u03bc\u03b9\u03bb\u03af\u03b1\u03c2 \u03b3\u03b9\u03b1 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf pad.", "pad.chat.title": "\u0386\u03bd\u03bf\u03b9\u03b3\u03bc\u03b1 \u03c4\u03b7\u03c2 \u03c3\u03c5\u03bd\u03bf\u03bc\u03b9\u03bb\u03af\u03b1\u03c2 \u03b3\u03b9\u03b1 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf pad.",
"pad.chat.loadmessages": "\u03a6\u03cc\u03c1\u03c4\u03c9\u03c3\u03b7 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03c9\u03bd \u03bc\u03b7\u03bd\u03c5\u03bc\u03ac\u03c4\u03c9\u03bd",
"timeslider.pageTitle": "{{appTitle}} \u03a7\u03c1\u03bf\u03bd\u03bf\u03b4\u03b9\u03ac\u03b3\u03c1\u03b1\u03bc\u03bc\u03b1", "timeslider.pageTitle": "{{appTitle}} \u03a7\u03c1\u03bf\u03bd\u03bf\u03b4\u03b9\u03ac\u03b3\u03c1\u03b1\u03bc\u03bc\u03b1",
"timeslider.toolbar.returnbutton": "\u0395\u03c0\u03b9\u03c3\u03c4\u03c1\u03bf\u03c6\u03ae \u03c3\u03c4\u03bf pad", "timeslider.toolbar.returnbutton": "\u0395\u03c0\u03b9\u03c3\u03c4\u03c1\u03bf\u03c6\u03ae \u03c3\u03c4\u03bf pad",
"timeslider.toolbar.authors": "\u03a3\u03c5\u03bd\u03c4\u03ac\u03ba\u03c4\u03b5\u03c2:", "timeslider.toolbar.authors": "\u03a3\u03c5\u03bd\u03c4\u03ac\u03ba\u03c4\u03b5\u03c2:",
@ -88,7 +89,7 @@
"timeslider.toolbar.exportlink.title": "\u0395\u03be\u03b1\u03b3\u03c9\u03b3\u03ae", "timeslider.toolbar.exportlink.title": "\u0395\u03be\u03b1\u03b3\u03c9\u03b3\u03ae",
"timeslider.exportCurrent": "\u0395\u03be\u03b1\u03b3\u03c9\u03b3\u03ae \u03c4\u03c1\u03ad\u03c7\u03bf\u03c5\u03c3\u03b1\u03c2 \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7\u03c2 \u03c9\u03c2:", "timeslider.exportCurrent": "\u0395\u03be\u03b1\u03b3\u03c9\u03b3\u03ae \u03c4\u03c1\u03ad\u03c7\u03bf\u03c5\u03c3\u03b1\u03c2 \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7\u03c2 \u03c9\u03c2:",
"timeslider.version": "\u0388\u03ba\u03b4\u03bf\u03c3\u03b7 {{version}}", "timeslider.version": "\u0388\u03ba\u03b4\u03bf\u03c3\u03b7 {{version}}",
"timeslider.saved": "\u0391\u03c0\u03bf\u03b8\u03b7\u03ba\u03b5\u03cd\u03c4\u03b7\u03ba\u03b5 {{day}} {{month}}, {{year}}", "timeslider.saved": "\u0391\u03c0\u03bf\u03b8\u03b7\u03ba\u03b5\u03cd\u03c4\u03b7\u03ba\u03b5 \u03c3\u03c4\u03b9\u03c2 {{day}} {{month}} {{year}}",
"timeslider.dateformat": "{{day}}\/{{month}}\/{{year}} {{hours}}:{{minutes}}:{{seconds}}", "timeslider.dateformat": "{{day}}\/{{month}}\/{{year}} {{hours}}:{{minutes}}:{{seconds}}",
"timeslider.month.january": "\u0399\u03b1\u03bd\u03bf\u03c5\u03b1\u03c1\u03af\u03bf\u03c5", "timeslider.month.january": "\u0399\u03b1\u03bd\u03bf\u03c5\u03b1\u03c1\u03af\u03bf\u03c5",
"timeslider.month.february": "\u03a6\u03b5\u03b2\u03c1\u03bf\u03c5\u03b1\u03c1\u03af\u03bf\u03c5", "timeslider.month.february": "\u03a6\u03b5\u03b2\u03c1\u03bf\u03c5\u03b1\u03c1\u03af\u03bf\u03c5",
@ -102,6 +103,8 @@
"timeslider.month.october": "\u039f\u03ba\u03c4\u03c9\u03b2\u03c1\u03af\u03bf\u03c5", "timeslider.month.october": "\u039f\u03ba\u03c4\u03c9\u03b2\u03c1\u03af\u03bf\u03c5",
"timeslider.month.november": "\u039d\u03bf\u03b5\u03bc\u03b2\u03c1\u03af\u03bf\u03c5", "timeslider.month.november": "\u039d\u03bf\u03b5\u03bc\u03b2\u03c1\u03af\u03bf\u03c5",
"timeslider.month.december": "\u0394\u03b5\u03ba\u03b5\u03bc\u03b2\u03c1\u03af\u03bf\u03c5", "timeslider.month.december": "\u0394\u03b5\u03ba\u03b5\u03bc\u03b2\u03c1\u03af\u03bf\u03c5",
"timeslider.unnamedauthor": "{{num}} \u03b1\u03bd\u03ce\u03bd\u03c5\u03bc\u03bf\u03c2 \u03c3\u03c5\u03b3\u03b3\u03c1\u03b1\u03c6\u03ad\u03b1\u03c2",
"timeslider.unnamedauthors": "{{num}} \u03b1\u03bd\u03ce\u03bd\u03c5\u03bc\u03bf\u03b9 \u03c3\u03c5\u03b3\u03b3\u03c1\u03b1\u03c6\u03b5\u03af\u03c2",
"pad.savedrevs.marked": "\u0391\u03c5\u03c4\u03ae \u03b7 \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7 \u03b5\u03c0\u03b9\u03c3\u03b7\u03bc\u03ac\u03bd\u03b8\u03b7\u03ba\u03b5 \u03c9\u03c2 \u03b1\u03c0\u03bf\u03b8\u03b7\u03ba\u03b5\u03c5\u03bc\u03ad\u03bd\u03b7 \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7", "pad.savedrevs.marked": "\u0391\u03c5\u03c4\u03ae \u03b7 \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7 \u03b5\u03c0\u03b9\u03c3\u03b7\u03bc\u03ac\u03bd\u03b8\u03b7\u03ba\u03b5 \u03c9\u03c2 \u03b1\u03c0\u03bf\u03b8\u03b7\u03ba\u03b5\u03c5\u03bc\u03ad\u03bd\u03b7 \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7",
"pad.userlist.entername": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03bf \u03cc\u03bd\u03bf\u03bc\u03ac \u03c3\u03b1\u03c2", "pad.userlist.entername": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03bf \u03cc\u03bd\u03bf\u03bc\u03ac \u03c3\u03b1\u03c2",
"pad.userlist.unnamed": "\u03b1\u03bd\u03ce\u03bd\u03c5\u03bc\u03bf\u03c2", "pad.userlist.unnamed": "\u03b1\u03bd\u03ce\u03bd\u03c5\u03bc\u03bf\u03c2",

View file

@ -4,8 +4,9 @@
"0": "Armando-Martin", "0": "Armando-Martin",
"1": "Jacobo", "1": "Jacobo",
"2": "Joker", "2": "Joker",
"4": "Vivaelcelta", "3": "Rubenwap",
"5": "Xuacu" "5": "Vivaelcelta",
"6": "Xuacu"
} }
}, },
"index.newPad": "Nuevo Pad", "index.newPad": "Nuevo Pad",
@ -104,6 +105,8 @@
"timeslider.month.october": "Octubre", "timeslider.month.october": "Octubre",
"timeslider.month.november": "Noviembre", "timeslider.month.november": "Noviembre",
"timeslider.month.december": "Diciembre", "timeslider.month.december": "Diciembre",
"timeslider.unnamedauthor": "{{num}} autor desconocido",
"timeslider.unnamedauthors": "{{num}} autores desconocidos",
"pad.savedrevs.marked": "Revisi\u00f3n guardada", "pad.savedrevs.marked": "Revisi\u00f3n guardada",
"pad.userlist.entername": "Escribe tu nombre", "pad.userlist.entername": "Escribe tu nombre",
"pad.userlist.unnamed": "an\u00f3nimo", "pad.userlist.unnamed": "an\u00f3nimo",

View file

@ -24,7 +24,7 @@
"pad.toolbar.clearAuthorship.title": "Poista kirjoittajav\u00e4rit", "pad.toolbar.clearAuthorship.title": "Poista kirjoittajav\u00e4rit",
"pad.toolbar.import_export.title": "Tuo tai vie eri tiedostomuodoista tai -muotoihin", "pad.toolbar.import_export.title": "Tuo tai vie eri tiedostomuodoista tai -muotoihin",
"pad.toolbar.timeslider.title": "Aikajana", "pad.toolbar.timeslider.title": "Aikajana",
"pad.toolbar.savedRevision.title": "Tallennetut versiot", "pad.toolbar.savedRevision.title": "Tallenna muutos",
"pad.toolbar.settings.title": "Asetukset", "pad.toolbar.settings.title": "Asetukset",
"pad.toolbar.embed.title": "Upota muistio", "pad.toolbar.embed.title": "Upota muistio",
"pad.toolbar.showusers.title": "N\u00e4yt\u00e4 muistion k\u00e4ytt\u00e4j\u00e4t", "pad.toolbar.showusers.title": "N\u00e4yt\u00e4 muistion k\u00e4ytt\u00e4j\u00e4t",
@ -105,6 +105,8 @@
"timeslider.month.october": "lokakuu", "timeslider.month.october": "lokakuu",
"timeslider.month.november": "marraskuu", "timeslider.month.november": "marraskuu",
"timeslider.month.december": "joulukuu", "timeslider.month.december": "joulukuu",
"timeslider.unnamedauthor": "{{num}} nimet\u00f6n tekij\u00e4",
"timeslider.unnamedauthors": "{{num}} nimet\u00f6nt\u00e4 tekij\u00e4\u00e4",
"pad.savedrevs.marked": "T\u00e4m\u00e4 versio on nyt merkitty tallennetuksi versioksi", "pad.savedrevs.marked": "T\u00e4m\u00e4 versio on nyt merkitty tallennetuksi versioksi",
"pad.userlist.entername": "Kirjoita nimesi", "pad.userlist.entername": "Kirjoita nimesi",
"pad.userlist.unnamed": "nimet\u00f6n", "pad.userlist.unnamed": "nimet\u00f6n",

View file

@ -109,6 +109,8 @@
"timeslider.month.october": "Octobre", "timeslider.month.october": "Octobre",
"timeslider.month.november": "Novembre", "timeslider.month.november": "Novembre",
"timeslider.month.december": "D\u00e9cembre", "timeslider.month.december": "D\u00e9cembre",
"timeslider.unnamedauthor": "{{num}} auteur anonyme",
"timeslider.unnamedauthors": "{{num}} auteurs anonymes",
"pad.savedrevs.marked": "Cette r\u00e9vision est maintenant marqu\u00e9e comme r\u00e9vision enregistr\u00e9e", "pad.savedrevs.marked": "Cette r\u00e9vision est maintenant marqu\u00e9e comme r\u00e9vision enregistr\u00e9e",
"pad.userlist.entername": "Entrez votre nom", "pad.userlist.entername": "Entrez votre nom",
"pad.userlist.unnamed": "sans nom", "pad.userlist.unnamed": "sans nom",

View file

@ -100,6 +100,8 @@
"timeslider.month.october": "outubro", "timeslider.month.october": "outubro",
"timeslider.month.november": "novembro", "timeslider.month.november": "novembro",
"timeslider.month.december": "decembro", "timeslider.month.december": "decembro",
"timeslider.unnamedauthor": "{{num}} autor an\u00f3nimo",
"timeslider.unnamedauthors": "{{num}} autores an\u00f3nimos",
"pad.savedrevs.marked": "Esta revisi\u00f3n est\u00e1 agora marcada como revisi\u00f3n gardada", "pad.savedrevs.marked": "Esta revisi\u00f3n est\u00e1 agora marcada como revisi\u00f3n gardada",
"pad.userlist.entername": "Insira o seu nome", "pad.userlist.entername": "Insira o seu nome",
"pad.userlist.unnamed": "an\u00f3nimo", "pad.userlist.unnamed": "an\u00f3nimo",

View file

@ -79,6 +79,7 @@
"pad.share.emebdcode": "\u05d4\u05d8\u05de\u05e2\u05ea \u05e7\u05d9\u05e9\u05d5\u05e8", "pad.share.emebdcode": "\u05d4\u05d8\u05de\u05e2\u05ea \u05e7\u05d9\u05e9\u05d5\u05e8",
"pad.chat": "\u05e9\u05d9\u05d7\u05d4", "pad.chat": "\u05e9\u05d9\u05d7\u05d4",
"pad.chat.title": "\u05e4\u05ea\u05d9\u05d7\u05ea \u05d4\u05e9\u05d9\u05d7\u05d4 \u05e9\u05dc \u05d4\u05e4\u05e0\u05e7\u05e1 \u05d4\u05d6\u05d4.", "pad.chat.title": "\u05e4\u05ea\u05d9\u05d7\u05ea \u05d4\u05e9\u05d9\u05d7\u05d4 \u05e9\u05dc \u05d4\u05e4\u05e0\u05e7\u05e1 \u05d4\u05d6\u05d4.",
"pad.chat.loadmessages": "\u05d8\u05e2\u05d9\u05e0\u05ea \u05d4\u05d5\u05d3\u05e2\u05d5\u05ea \u05e0\u05d5\u05e1\u05e4\u05d5\u05ea",
"timeslider.pageTitle": "\u05d2\u05d5\u05dc\u05dc \u05d6\u05de\u05df \u05e9\u05dc {{appTitle}}", "timeslider.pageTitle": "\u05d2\u05d5\u05dc\u05dc \u05d6\u05de\u05df \u05e9\u05dc {{appTitle}}",
"timeslider.toolbar.returnbutton": "\u05d7\u05d6\u05e8\u05d4 \u05d0\u05dc \u05d4\u05e4\u05e0\u05e7\u05e1", "timeslider.toolbar.returnbutton": "\u05d7\u05d6\u05e8\u05d4 \u05d0\u05dc \u05d4\u05e4\u05e0\u05e7\u05e1",
"timeslider.toolbar.authors": "\u05db\u05d5\u05ea\u05d1\u05d9\u05dd:", "timeslider.toolbar.authors": "\u05db\u05d5\u05ea\u05d1\u05d9\u05dd:",
@ -100,6 +101,8 @@
"timeslider.month.october": "\u05d0\u05d5\u05e7\u05d8\u05d5\u05d1\u05e8", "timeslider.month.october": "\u05d0\u05d5\u05e7\u05d8\u05d5\u05d1\u05e8",
"timeslider.month.november": "\u05e0\u05d5\u05d1\u05de\u05d1\u05e8", "timeslider.month.november": "\u05e0\u05d5\u05d1\u05de\u05d1\u05e8",
"timeslider.month.december": "\u05d3\u05e6\u05de\u05d1\u05e8", "timeslider.month.december": "\u05d3\u05e6\u05de\u05d1\u05e8",
"timeslider.unnamedauthor": "\u05db\u05d5\u05ea\u05d1 \u05d7\u05e1\u05e8\u05be\u05e9\u05dd \u05d0\u05d7\u05d3",
"timeslider.unnamedauthors": "{{num}} \u05db\u05d5\u05ea\u05d1\u05d9\u05dd \u05d7\u05e1\u05e8\u05d9\u05be\u05e9\u05dd",
"pad.savedrevs.marked": "\u05d2\u05e8\u05e1\u05d4 \u05d6\u05d5 \u05de\u05e1\u05d5\u05de\u05e0\u05ea \u05db\u05d2\u05e8\u05e1\u05d4 \u05e9\u05de\u05d5\u05e8\u05d4", "pad.savedrevs.marked": "\u05d2\u05e8\u05e1\u05d4 \u05d6\u05d5 \u05de\u05e1\u05d5\u05de\u05e0\u05ea \u05db\u05d2\u05e8\u05e1\u05d4 \u05e9\u05de\u05d5\u05e8\u05d4",
"pad.userlist.entername": "\u05e0\u05d0 \u05dc\u05d4\u05d6\u05d9\u05df \u05d0\u05ea \u05e9\u05de\u05da", "pad.userlist.entername": "\u05e0\u05d0 \u05dc\u05d4\u05d6\u05d9\u05df \u05d0\u05ea \u05e9\u05de\u05da",
"pad.userlist.unnamed": "\u05dc\u05dc\u05d0 \u05e9\u05dd", "pad.userlist.unnamed": "\u05dc\u05dc\u05d0 \u05e9\u05dd",

View file

@ -103,6 +103,8 @@
"timeslider.month.october": "ottobre", "timeslider.month.october": "ottobre",
"timeslider.month.november": "novembre", "timeslider.month.november": "novembre",
"timeslider.month.december": "dicembre", "timeslider.month.december": "dicembre",
"timeslider.unnamedauthor": "{{num}} autore senza nome",
"timeslider.unnamedauthors": "{{num}} autori senza nome",
"pad.savedrevs.marked": "Questa revisione \u00e8 ora contrassegnata come una versione salvata", "pad.savedrevs.marked": "Questa revisione \u00e8 ora contrassegnata come una versione salvata",
"pad.userlist.entername": "Inserisci il tuo nome", "pad.userlist.entername": "Inserisci il tuo nome",
"pad.userlist.unnamed": "senza nome", "pad.userlist.unnamed": "senza nome",

View file

@ -19,7 +19,7 @@
"pad.toolbar.clearAuthorship.title": "\u4f5c\u8005\u306e\u8272\u5206\u3051\u3092\u6d88\u53bb", "pad.toolbar.clearAuthorship.title": "\u4f5c\u8005\u306e\u8272\u5206\u3051\u3092\u6d88\u53bb",
"pad.toolbar.import_export.title": "\u4ed6\u306e\u5f62\u5f0f\u306e\u30d5\u30a1\u30a4\u30eb\u306e\u30a4\u30f3\u30dd\u30fc\u30c8\/\u30a8\u30af\u30b9\u30dd\u30fc\u30c8", "pad.toolbar.import_export.title": "\u4ed6\u306e\u5f62\u5f0f\u306e\u30d5\u30a1\u30a4\u30eb\u306e\u30a4\u30f3\u30dd\u30fc\u30c8\/\u30a8\u30af\u30b9\u30dd\u30fc\u30c8",
"pad.toolbar.timeslider.title": "\u30bf\u30a4\u30e0\u30b9\u30e9\u30a4\u30c0\u30fc", "pad.toolbar.timeslider.title": "\u30bf\u30a4\u30e0\u30b9\u30e9\u30a4\u30c0\u30fc",
"pad.toolbar.savedRevision.title": "\u4fdd\u5b58\u6e08\u307f\u306e\u7248", "pad.toolbar.savedRevision.title": "\u7248\u3092\u4fdd\u5b58",
"pad.toolbar.settings.title": "\u8a2d\u5b9a", "pad.toolbar.settings.title": "\u8a2d\u5b9a",
"pad.toolbar.embed.title": "\u3053\u306e\u30d1\u30c3\u30c9\u3092\u57cb\u3081\u8fbc\u3080", "pad.toolbar.embed.title": "\u3053\u306e\u30d1\u30c3\u30c9\u3092\u57cb\u3081\u8fbc\u3080",
"pad.toolbar.showusers.title": "\u3053\u306e\u30d1\u30c3\u30c9\u306e\u30e6\u30fc\u30b6\u30fc\u3092\u8868\u793a", "pad.toolbar.showusers.title": "\u3053\u306e\u30d1\u30c3\u30c9\u306e\u30e6\u30fc\u30b6\u30fc\u3092\u8868\u793a",
@ -87,7 +87,7 @@
"timeslider.exportCurrent": "\u73fe\u5728\u306e\u7248\u3092\u30a8\u30af\u30b9\u30dd\u30fc\u30c8\u3059\u308b\u5f62\u5f0f:", "timeslider.exportCurrent": "\u73fe\u5728\u306e\u7248\u3092\u30a8\u30af\u30b9\u30dd\u30fc\u30c8\u3059\u308b\u5f62\u5f0f:",
"timeslider.version": "\u30d0\u30fc\u30b8\u30e7\u30f3 {{version}}", "timeslider.version": "\u30d0\u30fc\u30b8\u30e7\u30f3 {{version}}",
"timeslider.saved": "| {{year}}\u5e74{{month}}{{day}}\u65e5\u306b\u4fdd\u5b58", "timeslider.saved": "| {{year}}\u5e74{{month}}{{day}}\u65e5\u306b\u4fdd\u5b58",
"timeslider.dateformat": "{{year}}\u5e74{{month}}{{day}}\u65e5 {{hours}}:{{minutes}}:{{seconds}}", "timeslider.dateformat": "{{year}}\u5e74{{month}}\u6708{{day}}\u65e5 {{hours}}:{{minutes}}:{{seconds}}",
"timeslider.month.january": "1\u6708", "timeslider.month.january": "1\u6708",
"timeslider.month.february": "2\u6708", "timeslider.month.february": "2\u6708",
"timeslider.month.march": "3\u6708", "timeslider.month.march": "3\u6708",
@ -100,6 +100,8 @@
"timeslider.month.october": "10\u6708", "timeslider.month.october": "10\u6708",
"timeslider.month.november": "11\u6708", "timeslider.month.november": "11\u6708",
"timeslider.month.december": "12\u6708", "timeslider.month.december": "12\u6708",
"timeslider.unnamedauthor": "{{num}} \u4eba\u306e\u533f\u540d\u306e\u4f5c\u8005",
"timeslider.unnamedauthors": "{{num}} \u4eba\u306e\u533f\u540d\u306e\u4f5c\u8005",
"pad.savedrevs.marked": "\u3053\u306e\u7248\u3092\u3001\u4fdd\u5b58\u6e08\u307f\u306e\u7248\u3068\u3057\u3066\u30de\u30fc\u30af\u3057\u307e\u3057\u305f\u3002", "pad.savedrevs.marked": "\u3053\u306e\u7248\u3092\u3001\u4fdd\u5b58\u6e08\u307f\u306e\u7248\u3068\u3057\u3066\u30de\u30fc\u30af\u3057\u307e\u3057\u305f\u3002",
"pad.userlist.entername": "\u540d\u524d\u3092\u5165\u529b", "pad.userlist.entername": "\u540d\u524d\u3092\u5165\u529b",
"pad.userlist.unnamed": "\u540d\u524d\u306a\u3057", "pad.userlist.unnamed": "\u540d\u524d\u306a\u3057",

View file

@ -100,6 +100,8 @@
"timeslider.month.october": "10\uc6d4", "timeslider.month.october": "10\uc6d4",
"timeslider.month.november": "11\uc6d4", "timeslider.month.november": "11\uc6d4",
"timeslider.month.december": "12\uc6d4", "timeslider.month.december": "12\uc6d4",
"timeslider.unnamedauthor": "\uc774\ub984 \uc5c6\ub294 \uc800\uc790 {{num}}\uba85",
"timeslider.unnamedauthors": "\uc774\ub984 \uc5c6\ub294 \uc800\uc790 {{num}}\uba85",
"pad.savedrevs.marked": "\uc774 \ud310\uc740 \uc774\uc81c \uc800\uc7a5\ud55c \ud310\uc73c\ub85c \ud45c\uc2dc\ud569\ub2c8\ub2e4.", "pad.savedrevs.marked": "\uc774 \ud310\uc740 \uc774\uc81c \uc800\uc7a5\ud55c \ud310\uc73c\ub85c \ud45c\uc2dc\ud569\ub2c8\ub2e4.",
"pad.userlist.entername": "\uc774\ub984\uc744 \uc785\ub825\ud558\uc138\uc694", "pad.userlist.entername": "\uc774\ub984\uc744 \uc785\ub825\ud558\uc138\uc694",
"pad.userlist.unnamed": "\uc774\ub984\uc5c6\uc74c", "pad.userlist.unnamed": "\uc774\ub984\uc5c6\uc74c",

View file

@ -19,13 +19,16 @@
"pad.toolbar.clearAuthorship.title": "d\u00e4 Schriiver ier F\u00e4rve fottn\u00e4mme", "pad.toolbar.clearAuthorship.title": "d\u00e4 Schriiver ier F\u00e4rve fottn\u00e4mme",
"pad.toolbar.import_export.title": "Vun ongerscheidlijje Dattei_Fommaate empotteere udder \u00e4xpotteere", "pad.toolbar.import_export.title": "Vun ongerscheidlijje Dattei_Fommaate empotteere udder \u00e4xpotteere",
"pad.toolbar.timeslider.title": "Verjangeheid afschpelle", "pad.toolbar.timeslider.title": "Verjangeheid afschpelle",
"pad.toolbar.savedRevision.title": "Fa\u00dfjehallde Versione", "pad.toolbar.savedRevision.title": "de Versjohn fa\u00dfhallde",
"pad.toolbar.settings.title": "Enscht\u00e4llonge", "pad.toolbar.settings.title": "Enscht\u00e4llonge",
"pad.toolbar.embed.title": "Donn dat Padd enbenge", "pad.toolbar.embed.title": "Donn dat Padd enbenge",
"pad.toolbar.showusers.title": "Verbonge Metschriiver aanzeije", "pad.toolbar.showusers.title": "Verbonge Metschriiver aanzeije",
"pad.colorpicker.save": "Fa\u00dfhallde", "pad.colorpicker.save": "Fa\u00dfhallde",
"pad.colorpicker.cancel": "Oph\u00fc\u00fcre", "pad.colorpicker.cancel": "Oph\u00fc\u00fcre",
"pad.loading": "Aam Laade&nbsp;&hellip;", "pad.loading": "Aam Laade&nbsp;&hellip;",
"pad.passwordRequired": "Do bruchs e Pa\u00dfwoot f\u00f6r heh dat P\u00e4dd.",
"pad.permissionDenied": "Do h\u00e4s nit dat R\u00e4\u00e4sch, op heh dat P\u00e4dd zohzejriife.",
"pad.wrongPassword": "Ding Pa\u00dfwoot wohr verkeht.",
"pad.settings.padSettings": "Dam P\u00e4dd sin Enscht\u00e4llonge", "pad.settings.padSettings": "Dam P\u00e4dd sin Enscht\u00e4llonge",
"pad.settings.myView": "Anseesch", "pad.settings.myView": "Anseesch",
"pad.settings.stickychat": "Donn der Klaaf emmer aanzeije", "pad.settings.stickychat": "Donn der Klaaf emmer aanzeije",
@ -38,6 +41,7 @@
"pad.settings.language": "Schprooch:", "pad.settings.language": "Schprooch:",
"pad.importExport.import_export": "Empoot\/\u00c4xpoot", "pad.importExport.import_export": "Empoot\/\u00c4xpoot",
"pad.importExport.import": "Donn jeede T\u00e4x udder jeede Zoot Dokem\u00e4nt huhlaade", "pad.importExport.import": "Donn jeede T\u00e4x udder jeede Zoot Dokem\u00e4nt huhlaade",
"pad.importExport.importSuccessful": "Jeschaff!",
"pad.importExport.export": "Don dat P\u00e4dd \u00e4xpoteere al\u00df:", "pad.importExport.export": "Don dat P\u00e4dd \u00e4xpoteere al\u00df:",
"pad.importExport.exporthtml": "HTML", "pad.importExport.exporthtml": "HTML",
"pad.importExport.exportplain": "Eijfach T\u00e4x", "pad.importExport.exportplain": "Eijfach T\u00e4x",
@ -45,9 +49,11 @@
"pad.importExport.exportpdf": "PDF (Poteerbaa Dokem\u00e4nte Fommaat)", "pad.importExport.exportpdf": "PDF (Poteerbaa Dokem\u00e4nte Fommaat)",
"pad.importExport.exportopen": "ODF (Offe Dokem\u00e4nte-Fommaat)", "pad.importExport.exportopen": "ODF (Offe Dokem\u00e4nte-Fommaat)",
"pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.exportdokuwiki": "DokuWiki",
"pad.importExport.abiword.innerHTML": "Mer k\u00fcnne blo\u00df eijfaache T\u00e4xte udder HTML_Fommaate empoteere. Opw\u00e4ndejere M\u00fcjjeleschkeite f\u00f6 der Empoot jon och, dof\u00f6r bruch mer en <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\">Enschtallazjuhn met <i lang=\"en\">Abiword<\/i><\/a>.",
"pad.modals.connected": "Verbonge.", "pad.modals.connected": "Verbonge.",
"pad.modals.reconnecting": "Ben wider aam Verbenge&nbsp;&hellip;", "pad.modals.reconnecting": "Ben wider aam Verbenge&nbsp;&hellip;",
"pad.modals.forcereconnect": "Wider verbenge", "pad.modals.forcereconnect": "Wider verbenge",
"pad.modals.userdup": "En enem andere Finster en \u00c4rbeid",
"pad.modals.userdup.explanation": "Heh dat Padd schingk en mieh wi einem Finster vun enem Brauser op heh d\u00e4m R\u00e4\u00e4schner op ze sin.", "pad.modals.userdup.explanation": "Heh dat Padd schingk en mieh wi einem Finster vun enem Brauser op heh d\u00e4m R\u00e4\u00e4schner op ze sin.",
"pad.modals.userdup.advice": "En heh d\u00e4m Finster wider verbenge.", "pad.modals.userdup.advice": "En heh d\u00e4m Finster wider verbenge.",
"pad.modals.unauth": "Nit ber\u00e4\u00e4schtesch", "pad.modals.unauth": "Nit ber\u00e4\u00e4schtesch",
@ -72,10 +78,12 @@
"pad.share.emebdcode": "URL enboue", "pad.share.emebdcode": "URL enboue",
"pad.chat": "Klaaf", "pad.chat": "Klaaf",
"pad.chat.title": "Maach d\u00e4 Klaaf f\u00f6r heh dat P\u00e4dd op", "pad.chat.title": "Maach d\u00e4 Klaaf f\u00f6r heh dat P\u00e4dd op",
"pad.chat.loadmessages": "Mieh Nohresschte laade...",
"timeslider.pageTitle": "{{appTitle}} - Verjangeheid affschpelle", "timeslider.pageTitle": "{{appTitle}} - Verjangeheid affschpelle",
"timeslider.toolbar.returnbutton": "Jangk retuur nohm P\u00e4dd", "timeslider.toolbar.returnbutton": "Jangk retuur nohm P\u00e4dd",
"timeslider.toolbar.authors": "Schriiver:", "timeslider.toolbar.authors": "Schriiver:",
"timeslider.toolbar.authorsList": "Kein Schriivere", "timeslider.toolbar.authorsList": "Kein Schriivere",
"timeslider.toolbar.exportlink.title": "\u00c4xpoot",
"timeslider.exportCurrent": "Donn de meu\u00dfte V\u00e4sjohn \u00e4xpotteere al\u00df:", "timeslider.exportCurrent": "Donn de meu\u00dfte V\u00e4sjohn \u00e4xpotteere al\u00df:",
"timeslider.version": "V\u00e4sjon {{version}}", "timeslider.version": "V\u00e4sjon {{version}}",
"timeslider.saved": "Fa\u00dfjehallde aam {{day}}. {{month}} {{year}}", "timeslider.saved": "Fa\u00dfjehallde aam {{day}}. {{month}} {{year}}",
@ -92,11 +100,19 @@
"timeslider.month.october": "Oktoober", "timeslider.month.october": "Oktoober",
"timeslider.month.november": "Nov\u00e4mber", "timeslider.month.november": "Nov\u00e4mber",
"timeslider.month.december": "Dez\u00e4mber", "timeslider.month.december": "Dez\u00e4mber",
"timeslider.unnamedauthor": "{{num}} naameloose Schriever",
"timeslider.unnamedauthors": "{{num}} naameloose Schriever",
"pad.savedrevs.marked": "Heh di V\u00e4sjohn es j\u00e4z fa\u00dfjehallde.",
"pad.userlist.entername": "Jif Dinge Naame en", "pad.userlist.entername": "Jif Dinge Naame en",
"pad.userlist.unnamed": "naamelo\u00df\u00df", "pad.userlist.unnamed": "naamelo\u00df\u00df",
"pad.userlist.guest": "Ja\u00df\u00df", "pad.userlist.guest": "Ja\u00df\u00df",
"pad.userlist.deny": "Aflehne", "pad.userlist.deny": "Aflehne",
"pad.userlist.approve": "Joodhei\u00dfe",
"pad.editbar.clearcolors": "Sulle mer de F\u00e4rve f\u00f6r de Schriiver uss_em janze T\u00e4x fott maache?",
"pad.impexp.importbutton": "J\u00e4z empoteere",
"pad.impexp.importing": "Ben aam Empotteere&nbsp;&hellip;", "pad.impexp.importing": "Ben aam Empotteere&nbsp;&hellip;",
"pad.impexp.confirmimport": "En Dattei ze empotteere m\u00e4\u00e4t der janze T\u00e4x em P\u00e4dd fott. Wess De dat verfaftesch hann?",
"pad.impexp.convertFailed": "Mer kunnte di Dattei nit empoteere. Nemm en ander Dattei-Fommaat udder donn d\u00e4 T\u00e4x vun Hand kopeere un ennf\u00f6\u00f6je.",
"pad.impexp.uploadFailed": "Et Huhlaade es don\u00e4vve jejange, bes esu jood un probeer et norr_ens", "pad.impexp.uploadFailed": "Et Huhlaade es don\u00e4vve jejange, bes esu jood un probeer et norr_ens",
"pad.impexp.importfailed": "Et Empoteere es don\u00e4vve jejange", "pad.impexp.importfailed": "Et Empoteere es don\u00e4vve jejange",
"pad.impexp.copypaste": "Bes esu jood un donn et koppeere un enf\u00f6\u00f6je", "pad.impexp.copypaste": "Bes esu jood un donn et koppeere un enf\u00f6\u00f6je",

View file

@ -101,6 +101,8 @@
"timeslider.month.october": "\u043e\u043a\u0442\u043e\u043c\u0432\u0440\u0438", "timeslider.month.october": "\u043e\u043a\u0442\u043e\u043c\u0432\u0440\u0438",
"timeslider.month.november": "\u043d\u043e\u0435\u043c\u0432\u0440\u0438", "timeslider.month.november": "\u043d\u043e\u0435\u043c\u0432\u0440\u0438",
"timeslider.month.december": "\u0434\u0435\u043a\u0435\u043c\u0432\u0440\u0438", "timeslider.month.december": "\u0434\u0435\u043a\u0435\u043c\u0432\u0440\u0438",
"timeslider.unnamedauthor": "{{num}} \u043d\u0435\u0438\u043c\u0435\u043d\u0443\u0432\u0430\u043d \u0430\u0432\u0442\u043e\u0440",
"timeslider.unnamedauthors": "{{num}} \u043d\u0435\u0438\u043c\u0435\u043d\u0443\u0432\u0430\u043d\u0438 \u0430\u0432\u0442\u043e\u0440\u0438",
"pad.savedrevs.marked": "\u041e\u0432\u0430\u0430 \u0440\u0435\u0432\u0438\u0437\u0438\u0458\u0430 \u0441\u0435\u0433\u0430 \u0435 \u043e\u0437\u043d\u0430\u0447\u0435\u043d\u0430 \u043a\u0430\u043a\u043e \u0437\u0430\u0447\u0443\u0432\u0430\u043d\u0430", "pad.savedrevs.marked": "\u041e\u0432\u0430\u0430 \u0440\u0435\u0432\u0438\u0437\u0438\u0458\u0430 \u0441\u0435\u0433\u0430 \u0435 \u043e\u0437\u043d\u0430\u0447\u0435\u043d\u0430 \u043a\u0430\u043a\u043e \u0437\u0430\u0447\u0443\u0432\u0430\u043d\u0430",
"pad.userlist.entername": "\u0412\u043d\u0435\u0441\u0435\u0442\u0435 \u0433\u043e \u0432\u0430\u0448\u0435\u0442\u043e \u0438\u043c\u0435", "pad.userlist.entername": "\u0412\u043d\u0435\u0441\u0435\u0442\u0435 \u0433\u043e \u0432\u0430\u0448\u0435\u0442\u043e \u0438\u043c\u0435",
"pad.userlist.unnamed": "\u0431\u0435\u0437 \u0438\u043c\u0435", "pad.userlist.unnamed": "\u0431\u0435\u0437 \u0438\u043c\u0435",

View file

@ -79,6 +79,7 @@
"pad.share.emebdcode": "\u0d0e\u0d02\u0d2c\u0d46\u0d21\u0d4d \u0d2f\u0d41.\u0d06\u0d7c.\u0d0e\u0d7d.", "pad.share.emebdcode": "\u0d0e\u0d02\u0d2c\u0d46\u0d21\u0d4d \u0d2f\u0d41.\u0d06\u0d7c.\u0d0e\u0d7d.",
"pad.chat": "\u0d24\u0d24\u0d4d\u0d38\u0d2e\u0d2f\u0d38\u0d02\u0d35\u0d3e\u0d26\u0d02", "pad.chat": "\u0d24\u0d24\u0d4d\u0d38\u0d2e\u0d2f\u0d38\u0d02\u0d35\u0d3e\u0d26\u0d02",
"pad.chat.title": "\u0d08 \u0d2a\u0d3e\u0d21\u0d3f\u0d28\u0d4d\u0d31\u0d46 \u0d24\u0d24\u0d4d\u0d38\u0d2e\u0d2f\u0d38\u0d02\u0d35\u0d3e\u0d26\u0d02 \u0d24\u0d41\u0d31\u0d15\u0d4d\u0d15\u0d41\u0d15.", "pad.chat.title": "\u0d08 \u0d2a\u0d3e\u0d21\u0d3f\u0d28\u0d4d\u0d31\u0d46 \u0d24\u0d24\u0d4d\u0d38\u0d2e\u0d2f\u0d38\u0d02\u0d35\u0d3e\u0d26\u0d02 \u0d24\u0d41\u0d31\u0d15\u0d4d\u0d15\u0d41\u0d15.",
"pad.chat.loadmessages": "\u0d15\u0d42\u0d1f\u0d41\u0d24\u0d7d \u0d38\u0d28\u0d4d\u0d26\u0d47\u0d36\u0d19\u0d4d\u0d19\u0d7e \u0d0e\u0d1f\u0d41\u0d15\u0d4d\u0d15\u0d41\u0d15",
"timeslider.pageTitle": "{{appTitle}} \u0d38\u0d2e\u0d2f\u0d30\u0d47\u0d16", "timeslider.pageTitle": "{{appTitle}} \u0d38\u0d2e\u0d2f\u0d30\u0d47\u0d16",
"timeslider.toolbar.returnbutton": "\u0d2a\u0d3e\u0d21\u0d3f\u0d32\u0d47\u0d15\u0d4d\u0d15\u0d4d \u0d24\u0d3f\u0d30\u0d3f\u0d1a\u0d4d\u0d1a\u0d41\u0d2a\u0d4b\u0d35\u0d41\u0d15", "timeslider.toolbar.returnbutton": "\u0d2a\u0d3e\u0d21\u0d3f\u0d32\u0d47\u0d15\u0d4d\u0d15\u0d4d \u0d24\u0d3f\u0d30\u0d3f\u0d1a\u0d4d\u0d1a\u0d41\u0d2a\u0d4b\u0d35\u0d41\u0d15",
"timeslider.toolbar.authors": "\u0d30\u0d1a\u0d2f\u0d3f\u0d24\u0d3e\u0d15\u0d4d\u0d15\u0d7e:", "timeslider.toolbar.authors": "\u0d30\u0d1a\u0d2f\u0d3f\u0d24\u0d3e\u0d15\u0d4d\u0d15\u0d7e:",

View file

@ -78,6 +78,7 @@
"pad.share.emebdcode": "Benamkan URL", "pad.share.emebdcode": "Benamkan URL",
"pad.chat": "Sembang", "pad.chat": "Sembang",
"pad.chat.title": "Buka ruang sembang untuk pad ini.", "pad.chat.title": "Buka ruang sembang untuk pad ini.",
"pad.chat.loadmessages": "Muatkan banyak lagi pesanan",
"timeslider.pageTitle": "Gelangsar Masa {{appTitle}}", "timeslider.pageTitle": "Gelangsar Masa {{appTitle}}",
"timeslider.toolbar.returnbutton": "Kembali ke pad", "timeslider.toolbar.returnbutton": "Kembali ke pad",
"timeslider.toolbar.authors": "Pengarang:", "timeslider.toolbar.authors": "Pengarang:",
@ -99,6 +100,8 @@
"timeslider.month.october": "Oktober", "timeslider.month.october": "Oktober",
"timeslider.month.november": "November", "timeslider.month.november": "November",
"timeslider.month.december": "Disember", "timeslider.month.december": "Disember",
"timeslider.unnamedauthor": "{{num}} orang pengarang awanama",
"timeslider.unnamedauthors": "{{num}} orang pengarang awanama",
"pad.savedrevs.marked": "Semakan ini telah ditandai sebagai semakan tersimpan", "pad.savedrevs.marked": "Semakan ini telah ditandai sebagai semakan tersimpan",
"pad.userlist.entername": "Taipkan nama anda", "pad.userlist.entername": "Taipkan nama anda",
"pad.userlist.unnamed": "tanpa nama", "pad.userlist.unnamed": "tanpa nama",

View file

@ -19,7 +19,7 @@
"pad.toolbar.clearAuthorship.title": "Kleuren auteurs wissen", "pad.toolbar.clearAuthorship.title": "Kleuren auteurs wissen",
"pad.toolbar.import_export.title": "Naar\/van andere opmaak exporteren\/importeren", "pad.toolbar.import_export.title": "Naar\/van andere opmaak exporteren\/importeren",
"pad.toolbar.timeslider.title": "Tijdlijn", "pad.toolbar.timeslider.title": "Tijdlijn",
"pad.toolbar.savedRevision.title": "Opgeslagen versies", "pad.toolbar.savedRevision.title": "Versie opslaan",
"pad.toolbar.settings.title": "Instellingen", "pad.toolbar.settings.title": "Instellingen",
"pad.toolbar.embed.title": "Pad insluiten", "pad.toolbar.embed.title": "Pad insluiten",
"pad.toolbar.showusers.title": "Gebruikers van dit pad weergeven", "pad.toolbar.showusers.title": "Gebruikers van dit pad weergeven",

View file

@ -19,7 +19,7 @@
"pad.toolbar.clearAuthorship.title": "\u0424\u044b\u0441\u0441\u04d5\u0434\u0436\u044b \u043d\u044b\u0441\u04d5\u043d\u0442\u0442\u04d5 \u0430\u0439\u0441\u044b\u043d\u04d5\u043d", "pad.toolbar.clearAuthorship.title": "\u0424\u044b\u0441\u0441\u04d5\u0434\u0436\u044b \u043d\u044b\u0441\u04d5\u043d\u0442\u0442\u04d5 \u0430\u0439\u0441\u044b\u043d\u04d5\u043d",
"pad.toolbar.import_export.title": "\u0418\u043c\u043f\u043e\u0440\u0442\/\u044d\u043a\u0441\u043f\u043e\u0440\u0442 \u04d5\u043d\u0434\u04d5\u0440 \u0444\u0430\u0439\u043b\u044b \u0444\u043e\u0440\u043c\u0430\u0442\u0442\u04d5\u0439\/\u0444\u043e\u0440\u043c\u0430\u0442\u0442\u04d5\u043c", "pad.toolbar.import_export.title": "\u0418\u043c\u043f\u043e\u0440\u0442\/\u044d\u043a\u0441\u043f\u043e\u0440\u0442 \u04d5\u043d\u0434\u04d5\u0440 \u0444\u0430\u0439\u043b\u044b \u0444\u043e\u0440\u043c\u0430\u0442\u0442\u04d5\u0439\/\u0444\u043e\u0440\u043c\u0430\u0442\u0442\u04d5\u043c",
"pad.toolbar.timeslider.title": "\u0420\u04d5\u0441\u0442\u04d5\u0434\u0436\u044b \u0445\u0430\u0445\u0445", "pad.toolbar.timeslider.title": "\u0420\u04d5\u0441\u0442\u04d5\u0434\u0436\u044b \u0445\u0430\u0445\u0445",
"pad.toolbar.savedRevision.title": "\u04d4\u0432\u04d5\u0440\u0434 \u0444\u04d5\u043b\u0442\u04d5\u0440\u0442\u04d5", "pad.toolbar.savedRevision.title": "\u0424\u04d5\u043b\u0442\u04d5\u0440 \u0431\u0430\u0432\u04d5\u0440\u044b\u043d\u04d5\u043d",
"pad.toolbar.settings.title": "\u0423\u0430\u0433\u04d5\u0432\u04d5\u0440\u0434\u0442\u04d5", "pad.toolbar.settings.title": "\u0423\u0430\u0433\u04d5\u0432\u04d5\u0440\u0434\u0442\u04d5",
"pad.toolbar.embed.title": "\u0410\u0446\u044b \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442 \u0431\u0430\u0444\u0442\u0430\u0443\u044b\u043d", "pad.toolbar.embed.title": "\u0410\u0446\u044b \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442 \u0431\u0430\u0444\u0442\u0430\u0443\u044b\u043d",
"pad.toolbar.showusers.title": "\u0410\u0446\u044b \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b \u0430\u0440\u0445\u0430\u0439\u0434\u0436\u044b\u0442\u044b \u0440\u0430\u0432\u0434\u0438\u0441\u044b\u043d", "pad.toolbar.showusers.title": "\u0410\u0446\u044b \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b \u0430\u0440\u0445\u0430\u0439\u0434\u0436\u044b\u0442\u044b \u0440\u0430\u0432\u0434\u0438\u0441\u044b\u043d",
@ -78,6 +78,7 @@
"pad.share.emebdcode": "URL \u0431\u0430\u0432\u04d5\u0440\u044b\u043d", "pad.share.emebdcode": "URL \u0431\u0430\u0432\u04d5\u0440\u044b\u043d",
"pad.chat": "\u041d\u044b\u0445\u0430\u0441", "pad.chat": "\u041d\u044b\u0445\u0430\u0441",
"pad.chat.title": "\u041e\u0446\u044b \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u04d5\u043d \u0447\u0430\u0442 \u0431\u0430\u043a\u04d5\u043d.", "pad.chat.title": "\u041e\u0446\u044b \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u04d5\u043d \u0447\u0430\u0442 \u0431\u0430\u043a\u04d5\u043d.",
"pad.chat.loadmessages": "\u0424\u044b\u043b\u0434\u04d5\u0440 \u0444\u044b\u0441\u0442\u04d5\u0433 \u0440\u0430\u0432\u0433\u04d5\u043d\u044b\u043d",
"timeslider.pageTitle": "{{appTitle}}-\u044b \u0440\u04d5\u0442\u04d5\u0434\u0436\u044b \u0445\u0430\u0445\u0445", "timeslider.pageTitle": "{{appTitle}}-\u044b \u0440\u04d5\u0442\u04d5\u0434\u0436\u044b \u0445\u0430\u0445\u0445",
"timeslider.toolbar.returnbutton": "\u0424\u04d5\u0441\u0442\u04d5\u043c\u04d5, \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043c\u04d5", "timeslider.toolbar.returnbutton": "\u0424\u04d5\u0441\u0442\u04d5\u043c\u04d5, \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043c\u04d5",
"timeslider.toolbar.authors": "\u0424\u044b\u0441\u0441\u04d5\u0434\u0436\u044b\u0442\u04d5:", "timeslider.toolbar.authors": "\u0424\u044b\u0441\u0441\u04d5\u0434\u0436\u044b\u0442\u04d5:",
@ -99,6 +100,8 @@
"timeslider.month.october": "\u043e\u043a\u0442\u044f\u0431\u0440\u044c", "timeslider.month.october": "\u043e\u043a\u0442\u044f\u0431\u0440\u044c",
"timeslider.month.november": "\u043d\u043e\u044f\u0431\u0440\u044c", "timeslider.month.november": "\u043d\u043e\u044f\u0431\u0440\u044c",
"timeslider.month.december": "\u0434\u0435\u043a\u0430\u0431\u0440\u044c", "timeslider.month.december": "\u0434\u0435\u043a\u0430\u0431\u0440\u044c",
"timeslider.unnamedauthor": "{{num}} \u04d5\u043d\u04d5\u043d\u043e\u043c \u0444\u044b\u0441\u0441\u04d5\u0433",
"timeslider.unnamedauthors": "{{num}} \u04d5\u043d\u04d5\u043d\u043e\u043c \u0444\u044b\u0441\u0441\u04d5\u0434\u0436\u044b",
"pad.savedrevs.marked": "\u0410\u0446\u044b \u0444\u04d5\u043b\u0442\u04d5\u0440 \u043d\u044b\u0440 \u043a\u0443\u044b\u0434 \u04d5\u0432\u04d5\u0440\u0434 \u0444\u04d5\u043b\u0442\u04d5\u0440 \u043d\u044b\u0441\u0430\u043d\u0433\u043e\u043d\u0434 \u04d5\u0440\u0446\u044b\u0434", "pad.savedrevs.marked": "\u0410\u0446\u044b \u0444\u04d5\u043b\u0442\u04d5\u0440 \u043d\u044b\u0440 \u043a\u0443\u044b\u0434 \u04d5\u0432\u04d5\u0440\u0434 \u0444\u04d5\u043b\u0442\u04d5\u0440 \u043d\u044b\u0441\u0430\u043d\u0433\u043e\u043d\u0434 \u04d5\u0440\u0446\u044b\u0434",
"pad.userlist.entername": "\u0414\u04d5 \u043d\u043e\u043c \u0431\u0430\u0444\u044b\u0441\u0441", "pad.userlist.entername": "\u0414\u04d5 \u043d\u043e\u043c \u0431\u0430\u0444\u044b\u0441\u0441",
"pad.userlist.unnamed": "\u04d5\u043d\u04d5\u043d\u043e\u043c", "pad.userlist.unnamed": "\u04d5\u043d\u04d5\u043d\u043e\u043c",

View file

@ -21,7 +21,7 @@
"pad.toolbar.clearAuthorship.title": "Usu\u0144 kolory autor\u00f3w", "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.import_export.title": "Import\/eksport z\/do r\u00f3\u017cnych format\u00f3w plik\u00f3w",
"pad.toolbar.timeslider.title": "O\u015b czasu", "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.settings.title": "Ustawienia",
"pad.toolbar.embed.title": "Umie\u015b\u0107 ten Notatnik", "pad.toolbar.embed.title": "Umie\u015b\u0107 ten Notatnik",
"pad.toolbar.showusers.title": "Poka\u017c u\u017cytkownik\u00f3w", "pad.toolbar.showusers.title": "Poka\u017c u\u017cytkownik\u00f3w",
@ -102,6 +102,8 @@
"timeslider.month.october": "Pa\u017adziernik", "timeslider.month.october": "Pa\u017adziernik",
"timeslider.month.november": "Listopad", "timeslider.month.november": "Listopad",
"timeslider.month.december": "Grudzie\u0144", "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.savedrevs.marked": "Ta wersja zosta\u0142a w\u0142a\u015bnie oznaczona jako zapisana.",
"pad.userlist.entername": "Wprowad\u017a swoj\u0105 nazw\u0119", "pad.userlist.entername": "Wprowad\u017a swoj\u0105 nazw\u0119",
"pad.userlist.unnamed": "bez nazwy", "pad.userlist.unnamed": "bez nazwy",

View file

@ -17,7 +17,9 @@
"pad.settings.fontType": "\u0644\u064a\u06a9\u0628\u06bc\u06d0 \u0689\u0648\u0644:", "pad.settings.fontType": "\u0644\u064a\u06a9\u0628\u06bc\u06d0 \u0689\u0648\u0644:",
"pad.settings.fontType.normal": "\u0646\u0648\u0631\u0645\u0627\u0644", "pad.settings.fontType.normal": "\u0646\u0648\u0631\u0645\u0627\u0644",
"pad.settings.fontType.monospaced": "\u0645\u0648\u0646\u0648\u0633\u067e\u06d0\u0633", "pad.settings.fontType.monospaced": "\u0645\u0648\u0646\u0648\u0633\u067e\u06d0\u0633",
"pad.settings.globalView": "\u0646\u0693\u06d0\u0648\u0627\u0644\u0647 \u069a\u06a9\u0627\u0631\u06d0\u062f\u0646\u0647",
"pad.settings.language": "\u0698\u0628\u0647:", "pad.settings.language": "\u0698\u0628\u0647:",
"pad.importExport.importSuccessful": "\u0628\u0631\u064a\u0627\u0644\u06cc \u0634\u0648!",
"pad.importExport.exporthtml": "\u0627\u0686 \u067c\u064a \u0627\u0645 \u0627\u06d0\u0644", "pad.importExport.exporthtml": "\u0627\u0686 \u067c\u064a \u0627\u0645 \u0627\u06d0\u0644",
"pad.importExport.exportplain": "\u0633\u0627\u062f\u0647 \u0645\u062a\u0646", "pad.importExport.exportplain": "\u0633\u0627\u062f\u0647 \u0645\u062a\u0646",
"pad.importExport.exportword": "\u0645\u0627\u064a\u06a9\u0631\u0648\u0633\u0627\u0641\u067c \u0648\u0631\u0689", "pad.importExport.exportword": "\u0645\u0627\u064a\u06a9\u0631\u0648\u0633\u0627\u0641\u067c \u0648\u0631\u0689",

View file

@ -19,7 +19,7 @@
"pad.toolbar.clearAuthorship.title": "Limpar as cores de identifica\u00e7\u00e3o de autoria", "pad.toolbar.clearAuthorship.title": "Limpar as cores de identifica\u00e7\u00e3o de autoria",
"pad.toolbar.import_export.title": "Importar\/Exportar de\/para diferentes formatos de arquivo", "pad.toolbar.import_export.title": "Importar\/Exportar de\/para diferentes formatos de arquivo",
"pad.toolbar.timeslider.title": "Linha do tempo", "pad.toolbar.timeslider.title": "Linha do tempo",
"pad.toolbar.savedRevision.title": "Revis\u00f5es Salvas", "pad.toolbar.savedRevision.title": "Salvar revis\u00e3o",
"pad.toolbar.settings.title": "Configura\u00e7\u00f5es", "pad.toolbar.settings.title": "Configura\u00e7\u00f5es",
"pad.toolbar.embed.title": "Incorporar esta Nota", "pad.toolbar.embed.title": "Incorporar esta Nota",
"pad.toolbar.showusers.title": "Mostrar os usuarios nesta Nota", "pad.toolbar.showusers.title": "Mostrar os usuarios nesta Nota",
@ -100,6 +100,8 @@
"timeslider.month.october": "Outubro", "timeslider.month.october": "Outubro",
"timeslider.month.november": "Novembro", "timeslider.month.november": "Novembro",
"timeslider.month.december": "Dezembro", "timeslider.month.december": "Dezembro",
"timeslider.unnamedauthor": "{{num}} autor desconhecido",
"timeslider.unnamedauthors": "{{num}} autores desconhecidos",
"pad.savedrevs.marked": "Esta revis\u00e3o foi marcada como salva", "pad.savedrevs.marked": "Esta revis\u00e3o foi marcada como salva",
"pad.userlist.entername": "Insira o seu nome", "pad.userlist.entername": "Insira o seu nome",
"pad.userlist.unnamed": "Sem t\u00edtulo", "pad.userlist.unnamed": "Sem t\u00edtulo",

View file

@ -103,6 +103,8 @@
"timeslider.month.october": "\u043e\u043a\u0442\u044f\u0431\u0440\u044c", "timeslider.month.october": "\u043e\u043a\u0442\u044f\u0431\u0440\u044c",
"timeslider.month.november": "\u043d\u043e\u044f\u0431\u0440\u044c", "timeslider.month.november": "\u043d\u043e\u044f\u0431\u0440\u044c",
"timeslider.month.december": "\u0434\u0435\u043a\u0430\u0431\u0440\u044c", "timeslider.month.december": "\u0434\u0435\u043a\u0430\u0431\u0440\u044c",
"timeslider.unnamedauthor": "{{num}} \u0431\u0435\u0437\u044b\u043c\u044f\u043d\u043d\u044b\u0439 \u0430\u0432\u0442\u043e\u0440",
"timeslider.unnamedauthors": "\u0431\u0435\u0437\u044b\u043c\u044f\u043d\u043d\u044b\u0445 \u0430\u0432\u0442\u043e\u0440\u043e\u0432: {{num}}",
"pad.savedrevs.marked": "\u042d\u0442\u0430 \u0432\u0435\u0440\u0441\u0438\u044f \u0442\u0435\u043f\u0435\u0440\u044c \u043f\u043e\u043c\u0435\u0447\u0435\u043d\u0430 \u043a\u0430\u043a \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u043d\u0430\u044f", "pad.savedrevs.marked": "\u042d\u0442\u0430 \u0432\u0435\u0440\u0441\u0438\u044f \u0442\u0435\u043f\u0435\u0440\u044c \u043f\u043e\u043c\u0435\u0447\u0435\u043d\u0430 \u043a\u0430\u043a \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u043d\u0430\u044f",
"pad.userlist.entername": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0432\u0430\u0448\u0435 \u0438\u043c\u044f", "pad.userlist.entername": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0432\u0430\u0448\u0435 \u0438\u043c\u044f",
"pad.userlist.unnamed": "\u0431\u0435\u0437\u044b\u043c\u044f\u043d\u043d\u044b\u0439", "pad.userlist.unnamed": "\u0431\u0435\u0437\u044b\u043c\u044f\u043d\u043d\u044b\u0439",

View file

@ -100,6 +100,8 @@
"timeslider.month.october": "Oktober", "timeslider.month.october": "Oktober",
"timeslider.month.november": "November", "timeslider.month.november": "November",
"timeslider.month.december": "December", "timeslider.month.december": "December",
"timeslider.unnamedauthor": "neimenovani avtor {{num}}",
"timeslider.unnamedauthors": "{{num}} neimenovani avtorji",
"pad.savedrevs.marked": "Ta predelava je ozna\u010dena kot shranjena predelava.", "pad.savedrevs.marked": "Ta predelava je ozna\u010dena kot shranjena predelava.",
"pad.userlist.entername": "Vpi\u0161ite ime", "pad.userlist.entername": "Vpi\u0161ite ime",
"pad.userlist.unnamed": "neimenovana oseba", "pad.userlist.unnamed": "neimenovana oseba",

View file

@ -100,6 +100,8 @@
"timeslider.month.october": "oktober", "timeslider.month.october": "oktober",
"timeslider.month.november": "november", "timeslider.month.november": "november",
"timeslider.month.december": "december", "timeslider.month.december": "december",
"timeslider.unnamedauthor": "{{num}} namnl\u00f6s f\u00f6rfattare",
"timeslider.unnamedauthors": "{{num}} namnl\u00f6sa f\u00f6rfattare",
"pad.savedrevs.marked": "Denna revision \u00e4r nu markerad som en sparad revision", "pad.savedrevs.marked": "Denna revision \u00e4r nu markerad som en sparad revision",
"pad.userlist.entername": "Ange ditt namn", "pad.userlist.entername": "Ange ditt namn",
"pad.userlist.unnamed": "namnl\u00f6s", "pad.userlist.unnamed": "namnl\u00f6s",

View file

@ -38,6 +38,7 @@
"pad.settings.language": "\u0c2d\u0c3e\u0c37", "pad.settings.language": "\u0c2d\u0c3e\u0c37",
"pad.importExport.import_export": "\u0c26\u0c3f\u0c17\u0c41\u0c2e\u0c24\u0c3f\/\u0c0e\u0c17\u0c41\u0c2e\u0c24\u0c3f", "pad.importExport.import_export": "\u0c26\u0c3f\u0c17\u0c41\u0c2e\u0c24\u0c3f\/\u0c0e\u0c17\u0c41\u0c2e\u0c24\u0c3f",
"pad.importExport.import": "\u0c2a\u0c3e\u0c20\u0c2e\u0c41 \u0c26\u0c38\u0c4d\u0c24\u0c4d\u0c30\u0c2e\u0c41 \u0c32\u0c47\u0c26\u0c3e \u0c2a\u0c24\u0c4d\u0c30\u0c2e\u0c41\u0c28\u0c41 \u0c26\u0c3f\u0c17\u0c41\u0c2e\u0c24\u0c3f \u0c1a\u0c47\u0c2f\u0c41\u0c2e\u0c41", "pad.importExport.import": "\u0c2a\u0c3e\u0c20\u0c2e\u0c41 \u0c26\u0c38\u0c4d\u0c24\u0c4d\u0c30\u0c2e\u0c41 \u0c32\u0c47\u0c26\u0c3e \u0c2a\u0c24\u0c4d\u0c30\u0c2e\u0c41\u0c28\u0c41 \u0c26\u0c3f\u0c17\u0c41\u0c2e\u0c24\u0c3f \u0c1a\u0c47\u0c2f\u0c41\u0c2e\u0c41",
"pad.importExport.importSuccessful": "\u0c35\u0c3f\u0c1c\u0c2f\u0c35\u0c02\u0c24\u0c02!",
"pad.importExport.export": "\u0c2a\u0c4d\u0c30\u0c38\u0c4d\u0c24\u0c41\u0c24 \u0c2a\u0c32\u0c15\u0c28\u0c3f \u0c08 \u0c35\u0c3f\u0c27\u0c2e\u0c41\u0c17\u0c3e \u0c0e\u0c17\u0c41\u0c2e\u0c24\u0c3f \u0c1a\u0c47\u0c2f\u0c41\u0c2e\u0c41:", "pad.importExport.export": "\u0c2a\u0c4d\u0c30\u0c38\u0c4d\u0c24\u0c41\u0c24 \u0c2a\u0c32\u0c15\u0c28\u0c3f \u0c08 \u0c35\u0c3f\u0c27\u0c2e\u0c41\u0c17\u0c3e \u0c0e\u0c17\u0c41\u0c2e\u0c24\u0c3f \u0c1a\u0c47\u0c2f\u0c41\u0c2e\u0c41:",
"pad.importExport.exporthtml": "\u0c39\u0c46\u0c1a\u0c4d \u0c1f\u0c3f \u0c0e\u0c02 \u0c0e\u0c32\u0c4d", "pad.importExport.exporthtml": "\u0c39\u0c46\u0c1a\u0c4d \u0c1f\u0c3f \u0c0e\u0c02 \u0c0e\u0c32\u0c4d",
"pad.importExport.exportplain": "\u0c38\u0c3e\u0c26\u0c3e \u0c2a\u0c3e\u0c20\u0c4d\u0c2f\u0c02", "pad.importExport.exportplain": "\u0c38\u0c3e\u0c26\u0c3e \u0c2a\u0c3e\u0c20\u0c4d\u0c2f\u0c02",

View file

@ -102,6 +102,8 @@
"timeslider.month.october": "\u0416\u043e\u0432\u0442\u0435\u043d\u044c", "timeslider.month.october": "\u0416\u043e\u0432\u0442\u0435\u043d\u044c",
"timeslider.month.november": "\u041b\u0438\u0441\u0442\u043e\u043f\u0430\u0434", "timeslider.month.november": "\u041b\u0438\u0441\u0442\u043e\u043f\u0430\u0434",
"timeslider.month.december": "\u0413\u0440\u0443\u0434\u0435\u043d\u044c", "timeslider.month.december": "\u0413\u0440\u0443\u0434\u0435\u043d\u044c",
"timeslider.unnamedauthor": "{{num}} \u0431\u0435\u0437\u0456\u043c\u0435\u043d\u043d\u0438\u0439 \u0430\u0432\u0442\u043e\u0440",
"timeslider.unnamedauthors": "\u0431\u0435\u0437\u0456\u043c\u0435\u043d\u043d\u0438\u0445 \u0430\u0432\u0442\u043e\u0440\u043e\u0432: {{num}}",
"pad.savedrevs.marked": "\u0426\u044e \u0432\u0435\u0440\u0441\u0456\u044e \u043f\u043e\u043c\u0456\u0447\u0435\u043d\u043e \u0437\u0431\u0435\u0440\u0435\u0436\u0435\u043d\u043e\u044e \u0432\u0435\u0440\u0441\u0456\u0454\u044e", "pad.savedrevs.marked": "\u0426\u044e \u0432\u0435\u0440\u0441\u0456\u044e \u043f\u043e\u043c\u0456\u0447\u0435\u043d\u043e \u0437\u0431\u0435\u0440\u0435\u0436\u0435\u043d\u043e\u044e \u0432\u0435\u0440\u0441\u0456\u0454\u044e",
"pad.userlist.entername": "\u0412\u0432\u0435\u0434\u0456\u0442\u044c \u0412\u0430\u0448\u0435 \u0456\u043c'\u044f", "pad.userlist.entername": "\u0412\u0432\u0435\u0434\u0456\u0442\u044c \u0412\u0430\u0448\u0435 \u0456\u043c'\u044f",
"pad.userlist.unnamed": "\u0431\u0435\u0437\u0456\u043c\u0435\u043d\u043d\u0438\u0439", "pad.userlist.unnamed": "\u0431\u0435\u0437\u0456\u043c\u0435\u043d\u043d\u0438\u0439",

View file

@ -3,6 +3,8 @@
"authors": [ "authors": [
"Dimension", "Dimension",
"Hydra", "Hydra",
"Yfdyh000",
"\u4e4c\u62c9\u8de8\u6c2a",
"\u71c3\u7389" "\u71c3\u7389"
] ]
}, },
@ -18,8 +20,9 @@
"pad.toolbar.undo.title": "\u64a4\u6d88 (Ctrl-Z)", "pad.toolbar.undo.title": "\u64a4\u6d88 (Ctrl-Z)",
"pad.toolbar.redo.title": "\u91cd\u505a (Ctrl-Y)", "pad.toolbar.redo.title": "\u91cd\u505a (Ctrl-Y)",
"pad.toolbar.clearAuthorship.title": "\u6e05\u9664\u4f5c\u540d\u989c\u8272", "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.timeslider.title": "\u65f6\u95f4\u8f74",
"pad.toolbar.savedRevision.title": "\u5df2\u4fdd\u5b58\u7684\u4fee\u8ba2", "pad.toolbar.savedRevision.title": "\u4fdd\u5b58\u4fee\u8ba2",
"pad.toolbar.settings.title": "\u8bbe\u7f6e", "pad.toolbar.settings.title": "\u8bbe\u7f6e",
"pad.toolbar.embed.title": "\u5d4c\u5165\u6b64\u8bb0\u4e8b\u672c", "pad.toolbar.embed.title": "\u5d4c\u5165\u6b64\u8bb0\u4e8b\u672c",
"pad.toolbar.showusers.title": "\u663e\u793a\u6b64\u8bb0\u4e8b\u672c\u7684\u7528\u6237", "pad.toolbar.showusers.title": "\u663e\u793a\u6b64\u8bb0\u4e8b\u672c\u7684\u7528\u6237",
@ -30,12 +33,19 @@
"pad.permissionDenied": "\u60a8\u6ca1\u6709\u89c2\u770b\u8fd9\u4e2a\u8bb0\u4e8b\u672c\u7684\u6743\u9650", "pad.permissionDenied": "\u60a8\u6ca1\u6709\u89c2\u770b\u8fd9\u4e2a\u8bb0\u4e8b\u672c\u7684\u6743\u9650",
"pad.wrongPassword": "\u60a8\u7684\u5bc6\u7801\u9519\u4e86", "pad.wrongPassword": "\u60a8\u7684\u5bc6\u7801\u9519\u4e86",
"pad.settings.padSettings": "\u8bb0\u4e8b\u672c\u8bbe\u7f6e", "pad.settings.padSettings": "\u8bb0\u4e8b\u672c\u8bbe\u7f6e",
"pad.settings.myView": "\u6211\u7684\u89c6\u7a97",
"pad.settings.stickychat": "\u603b\u662f\u5728\u5c4f\u5e55\u4e0a\u663e\u793a\u804a\u5929",
"pad.settings.colorcheck": "\u4f5c\u8005\u989c\u8272",
"pad.settings.linenocheck": "\u884c\u53f7", "pad.settings.linenocheck": "\u884c\u53f7",
"pad.settings.fontType": "\u5b57\u4f53\u7c7b\u578b\uff1a", "pad.settings.fontType": "\u5b57\u4f53\u7c7b\u578b\uff1a",
"pad.settings.fontType.normal": "\u6b63\u5e38", "pad.settings.fontType.normal": "\u6b63\u5e38",
"pad.settings.fontType.monospaced": "\u7b49\u5bbd\u5b57\u4f53",
"pad.settings.globalView": "\u6240\u6709\u4eba\u7684\u89c6\u7a97",
"pad.settings.language": "\u8bed\u8a00\uff1a", "pad.settings.language": "\u8bed\u8a00\uff1a",
"pad.importExport.import_export": "\u5bfc\u5165\/\u5bfc\u51fa", "pad.importExport.import_export": "\u5bfc\u5165\/\u5bfc\u51fa",
"pad.importExport.import": "\u4e0a\u8f7d\u4efb\u4f55\u6587\u5b57\u6863\u6216\u6587\u6863",
"pad.importExport.importSuccessful": "\u6210\u529f\uff01", "pad.importExport.importSuccessful": "\u6210\u529f\uff01",
"pad.importExport.export": "\u5bfc\u51fa\u76ee\u524d\u7684\u8bb0\u4e8b\u7c3f\u4e3a\uff1a",
"pad.importExport.exporthtml": "HTML", "pad.importExport.exporthtml": "HTML",
"pad.importExport.exportplain": "\u7eaf\u6587\u672c", "pad.importExport.exportplain": "\u7eaf\u6587\u672c",
"pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportword": "Microsoft Word",
@ -43,21 +53,36 @@
"pad.importExport.exportopen": "ODF\uff08\u5f00\u653e\u6587\u6863\u683c\u5f0f\uff09", "pad.importExport.exportopen": "ODF\uff08\u5f00\u653e\u6587\u6863\u683c\u5f0f\uff09",
"pad.importExport.exportdokuwiki": "DokuWiki", "pad.importExport.exportdokuwiki": "DokuWiki",
"pad.modals.connected": "\u5df2\u8fde\u63a5\u3002", "pad.modals.connected": "\u5df2\u8fde\u63a5\u3002",
"pad.modals.reconnecting": "\u91cd\u65b0\u8fde\u63a5\u5230\u60a8\u7684\u8bb0\u4e8b\u7c3f...",
"pad.modals.forcereconnect": "\u5f3a\u5236\u91cd\u65b0\u8fde\u63a5", "pad.modals.forcereconnect": "\u5f3a\u5236\u91cd\u65b0\u8fde\u63a5",
"pad.modals.userdup": "\u5728\u53e6\u4e00\u4e2a\u89c6\u7a97\u4e2d\u6253\u5f00",
"pad.modals.userdup.explanation": "\u6b64\u8bb0\u4e8b\u7c3f\u4f3c\u4e4e\u5728\u6b64\u7535\u8111\u4e0a\u5728\u591a\u4e2a\u6d4f\u89c8\u5668\u89c6\u7a97\u4e2d\u6253\u5f00\u3002",
"pad.modals.userdup.advice": "\u91cd\u65b0\u8fde\u63a5\u5230\u6b64\u89c6\u7a97\u3002",
"pad.modals.unauth": "\u672a\u6388\u6743", "pad.modals.unauth": "\u672a\u6388\u6743",
"pad.modals.looping": "\u5df2\u79bb\u7ebf\u3002", "pad.modals.looping": "\u5df2\u79bb\u7ebf\u3002",
"pad.modals.initsocketfail": "\u65e0\u6cd5\u8bbf\u95ee\u670d\u52a1\u5668\u3002",
"pad.modals.initsocketfail.explanation": "\u65e0\u6cd5\u8fde\u63a5\u5230\u540c\u6b65\u670d\u52a1\u5668\u3002",
"pad.modals.initsocketfail.cause": "\u8fd9\u53ef\u80fd\u662f\u7531\u4e8e\u60a8\u7684\u6d4f\u89c8\u5668\u6216\u60a8\u7684\u4e92\u8054\u7f51\u8fde\u63a5\u7684\u95ee\u9898\u3002",
"pad.modals.slowcommit": "\u5df2\u79bb\u7ebf\u3002", "pad.modals.slowcommit": "\u5df2\u79bb\u7ebf\u3002",
"pad.modals.slowcommit.explanation": "\u670d\u52a1\u5668\u6ca1\u6709\u54cd\u5e94\u3002",
"pad.modals.slowcommit.cause": "\u8fd9\u53ef\u80fd\u662f\u7531\u4e8e\u7f51\u7edc\u8fde\u63a5\u95ee\u9898\u3002",
"pad.modals.deleted": "\u5df2\u522a\u9664\u3002", "pad.modals.deleted": "\u5df2\u522a\u9664\u3002",
"pad.modals.deleted.explanation": "\u6b64\u8bb0\u4e8b\u672c\u5df2\u88ab\u79fb\u9664\u3002", "pad.modals.deleted.explanation": "\u6b64\u8bb0\u4e8b\u672c\u5df2\u88ab\u79fb\u9664\u3002",
"pad.modals.disconnected": "\u60a8\u5df2\u88ab\u79bb\u7ebf\u3002", "pad.modals.disconnected": "\u60a8\u5df2\u88ab\u79bb\u7ebf\u3002",
"pad.modals.disconnected.explanation": "\u5230\u670d\u52a1\u5668\u7684\u8fde\u63a5\u5df2\u4e22\u5931",
"pad.modals.disconnected.cause": "\u670d\u52a1\u5668\u53ef\u80fd\u65e0\u6cd5\u4f7f\u7528\u3002\u82e5\u6b64\u60c5\u51b5\u6301\u7eed\u53d1\u751f\uff0c\u8bf7\u901a\u77e5\u6211\u4eec\u3002",
"pad.share": "\u5206\u4eab\u6b64\u8bb0\u4e8b\u672c", "pad.share": "\u5206\u4eab\u6b64\u8bb0\u4e8b\u672c",
"pad.share.readonly": "\u53ea\u80fd\u8bfb", "pad.share.readonly": "\u53ea\u80fd\u8bfb",
"pad.share.link": "\u94fe\u63a5", "pad.share.link": "\u94fe\u63a5",
"pad.share.emebdcode": "\u5d4c\u5165\u7f51\u5740", "pad.share.emebdcode": "\u5d4c\u5165\u7f51\u5740",
"pad.chat": "\u804a\u5929", "pad.chat": "\u804a\u5929",
"pad.chat.title": "\u6253\u5f00\u6b64\u8bb0\u4e8b\u7c3f\u7684\u804a\u5929\u3002",
"pad.chat.loadmessages": "\u52a0\u8f7d\u66f4\u591a\u4fe1\u606f",
"timeslider.toolbar.returnbutton": "\u8fd4\u56de\u8bb0\u4e8b\u672c", "timeslider.toolbar.returnbutton": "\u8fd4\u56de\u8bb0\u4e8b\u672c",
"timeslider.toolbar.authors": "\u4f5c\u8005\uff1a", "timeslider.toolbar.authors": "\u4f5c\u8005\uff1a",
"timeslider.toolbar.authorsList": "\u6ca1\u6709\u4f5c\u8005", "timeslider.toolbar.authorsList": "\u6ca1\u6709\u4f5c\u8005",
"timeslider.toolbar.exportlink.title": "\u5bfc\u51fa",
"timeslider.exportCurrent": "\u5bfc\u51fa\u76ee\u524d\u7248\u672c\u4e3a\uff1a",
"timeslider.version": "\u7b2c {{version}} \u7248\u672c", "timeslider.version": "\u7b2c {{version}} \u7248\u672c",
"timeslider.saved": "\u5728{{year}}\u5e74{{month}}{{day}}\u65e5\u4fdd\u5b58", "timeslider.saved": "\u5728{{year}}\u5e74{{month}}{{day}}\u65e5\u4fdd\u5b58",
"timeslider.month.january": "\u4e00\u6708", "timeslider.month.january": "\u4e00\u6708",
@ -72,13 +97,20 @@
"timeslider.month.october": "\u5341\u6708", "timeslider.month.october": "\u5341\u6708",
"timeslider.month.november": "\u5341\u4e00\u6708", "timeslider.month.november": "\u5341\u4e00\u6708",
"timeslider.month.december": "\u5341\u4e8c\u6708", "timeslider.month.december": "\u5341\u4e8c\u6708",
"timeslider.unnamedauthor": "{{num}}\u533f\u540d\u4f5c\u8005",
"timeslider.unnamedauthors": "{{num}}\u533f\u540d\u4f5c\u8005",
"pad.savedrevs.marked": "\u6b64\u4fee\u8ba2\u5df2\u6807\u8bb0\u4e3a\u4fdd\u5b58\u4fee\u8ba2",
"pad.userlist.entername": "\u8f93\u5165\u60a8\u7684\u59d3\u540d", "pad.userlist.entername": "\u8f93\u5165\u60a8\u7684\u59d3\u540d",
"pad.userlist.unnamed": "\u65e0\u540d", "pad.userlist.unnamed": "\u65e0\u540d",
"pad.userlist.guest": "\u8bbf\u5ba2", "pad.userlist.guest": "\u8bbf\u5ba2",
"pad.userlist.deny": "\u62d2\u7edd", "pad.userlist.deny": "\u62d2\u7edd",
"pad.userlist.approve": "\u6279\u51c6", "pad.userlist.approve": "\u6279\u51c6",
"pad.editbar.clearcolors": "\u6e05\u9664\u6574\u4e2a\u6587\u6863\u7684\u4f5c\u8005\u989c\u8272\u5417\uff1f",
"pad.impexp.importbutton": "\u73b0\u5728\u5bfc\u5165", "pad.impexp.importbutton": "\u73b0\u5728\u5bfc\u5165",
"pad.impexp.importing": "\u6b63\u5728\u5bfc\u5165...", "pad.impexp.importing": "\u6b63\u5728\u5bfc\u5165...",
"pad.impexp.convertFailed": "\u6211\u4eec\u65e0\u6cd5\u5bfc\u5165\u6b64\u6587\u6863\u3002\u8bf7\u4f7f\u7528\u4ed6\u6587\u6863\u683c\u5f0f\u6216\u624b\u52a8\u590d\u5236\u8d34\u4e0a\u3002",
"pad.impexp.uploadFailed": "\u4e0a\u8f7d\u5931\u8d25\uff0c\u8bf7\u91cd\u8bd5", "pad.impexp.uploadFailed": "\u4e0a\u8f7d\u5931\u8d25\uff0c\u8bf7\u91cd\u8bd5",
"pad.impexp.importfailed": "\u5bfc\u5165\u5931\u8d25" "pad.impexp.importfailed": "\u5bfc\u5165\u5931\u8d25",
"pad.impexp.copypaste": "\u8bf7\u590d\u5236\u7c98\u8d34",
"pad.impexp.exportdisabled": "{{type}} \u683c\u5f0f\u7684\u5bfc\u51fa\u88ab\u7981\u7528\u3002\u6709\u5173\u8be6\u60c5\uff0c\u8bf7\u4e0e\u60a8\u7684\u7cfb\u7edf\u7ba1\u7406\u5458\u8054\u7cfb\u3002"
} }

View file

@ -1,7 +1,8 @@
{ {
"@metadata": { "@metadata": {
"authors": { "authors": {
"1": "Simon Shek" "0": "Shirayuki",
"2": "Simon Shek"
} }
}, },
"index.newPad": "\u65b0Pad", "index.newPad": "\u65b0Pad",
@ -19,7 +20,7 @@
"pad.toolbar.clearAuthorship.title": "\u6e05\u9664\u4f5c\u540d\u984f\u8272", "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.import_export.title": "\u4ee5\u5176\u4ed6\u6a94\u6848\u683c\u5f0f\u5c0e\u5165\uff0f\u532f\u51fa",
"pad.toolbar.timeslider.title": "\u6642\u9593\u8ef8", "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.settings.title": "\u8a2d\u5b9a",
"pad.toolbar.embed.title": "\u5d4c\u5165\u6b64pad", "pad.toolbar.embed.title": "\u5d4c\u5165\u6b64pad",
"pad.toolbar.showusers.title": "\u986f\u793a\u6b64pad\u7684\u7528\u6236", "pad.toolbar.showusers.title": "\u986f\u793a\u6b64pad\u7684\u7528\u6236",
@ -86,8 +87,8 @@
"timeslider.toolbar.exportlink.title": "\u532f\u51fa", "timeslider.toolbar.exportlink.title": "\u532f\u51fa",
"timeslider.exportCurrent": "\u532f\u51fa\u7576\u524d\u7248\u672c\u70ba\uff1a", "timeslider.exportCurrent": "\u532f\u51fa\u7576\u524d\u7248\u672c\u70ba\uff1a",
"timeslider.version": "\u7248\u672c{{version}}", "timeslider.version": "\u7248\u672c{{version}}",
"timeslider.saved": "{{year}}{{month}}{{day}}\u4fdd\u5b58", "timeslider.saved": "{{year}}\u5e74{{month}}{{day}}\u65e5\u4fdd\u5b58",
"timeslider.dateformat": "{{year}}{{month}}{{day}} {{hours}}:{{minutes}}:{{seconds}}", "timeslider.dateformat": "{{year}}\u5e74{{month}}\u6708{{day}}\u65e5 {{hours}}:{{minutes}}:{{seconds}}",
"timeslider.month.january": "1\u6708", "timeslider.month.january": "1\u6708",
"timeslider.month.february": "2\u6708", "timeslider.month.february": "2\u6708",
"timeslider.month.march": "3\u6708", "timeslider.month.march": "3\u6708",
@ -100,6 +101,8 @@
"timeslider.month.october": "10\u6708", "timeslider.month.october": "10\u6708",
"timeslider.month.november": "11\u6708", "timeslider.month.november": "11\u6708",
"timeslider.month.december": "12\u6708", "timeslider.month.december": "12\u6708",
"timeslider.unnamedauthor": "{{num}} \u533f\u540d\u4f5c\u8005",
"timeslider.unnamedauthors": "{{num}} \u533f\u540d\u4f5c\u8005",
"pad.savedrevs.marked": "\u6b64\u4fee\u8a02\u5df2\u6a19\u8a18\u70ba\u5df2\u4fdd\u5b58\u3002", "pad.savedrevs.marked": "\u6b64\u4fee\u8a02\u5df2\u6a19\u8a18\u70ba\u5df2\u4fdd\u5b58\u3002",
"pad.userlist.entername": "\u8f38\u5165\u60a8\u7684\u59d3\u540d", "pad.userlist.entername": "\u8f38\u5165\u60a8\u7684\u59d3\u540d",
"pad.userlist.unnamed": "\u672a\u547d\u540d", "pad.userlist.unnamed": "\u672a\u547d\u540d",

View file

@ -253,9 +253,7 @@ exports.getHTML = function(padID, rev, callback)
exportHtml.getPadHTML(pad, undefined, function (err, html) exportHtml.getPadHTML(pad, undefined, function (err, html)
{ {
if(ERR(err, callback)) return; if(ERR(err, callback)) return;
data = {html: html}; data = {html: html};
callback(null, data); callback(null, data);
}); });
} }
@ -325,17 +323,17 @@ exports.getChatHistory = function(padID, start, end, callback)
if(!start || !end) if(!start || !end)
{ {
start = 0; start = 0;
end = pad.chatHead - 1; end = pad.chatHead;
} }
if(start >= chatHead) if(start >= chatHead && chatHead > 0)
{ {
callback(new customError("start is higher or equal to the current chatHead","apierror")); callback(new customError("start is higher or equal to the current chatHead","apierror"));
return; return;
} }
if(end >= chatHead) if(end > chatHead)
{ {
callback(new customError("end is higher or equal to the current chatHead","apierror")); callback(new customError("end is higher than the current chatHead","apierror"));
return; return;
} }

View file

@ -151,7 +151,6 @@ exports.getPad = function(id, text, callback)
pad.init(text, function(err) pad.init(text, function(err)
{ {
if(ERR(err, callback)) return; if(ERR(err, callback)) return;
globalPads.set(id, pad); globalPads.set(id, pad);
callback(null, pad); callback(null, pad);
}); });

View file

@ -1,5 +1,5 @@
/** /**
* The Session Manager provides functions to manage session in the database * The Session Manager provides functions to manage session in the database, it only provides session management for sessions created by the API
*/ */
/* /*

View file

@ -0,0 +1,82 @@
/*
* Stores session data in the database
* Source; https://github.com/edy-b/SciFlowWriter/blob/develop/available_plugins/ep_sciflowwriter/db/DirtyStore.js
* This is not used for authors that are created via the API at current
*/
var Store = require('ep_etherpad-lite/node_modules/connect/lib/middleware/session/store'),
utils = require('ep_etherpad-lite/node_modules/connect/lib/utils'),
Session = require('ep_etherpad-lite/node_modules/connect/lib/middleware/session/session'),
db = require('ep_etherpad-lite/node/db/DB').db,
log4js = require('ep_etherpad-lite/node_modules/log4js'),
messageLogger = log4js.getLogger("SessionStore");
var SessionStore = module.exports = function SessionStore() {};
SessionStore.prototype.__proto__ = Store.prototype;
SessionStore.prototype.get = function(sid, fn){
messageLogger.debug('GET ' + sid);
var self = this;
db.get("sessionstorage:" + sid, function (err, sess)
{
if (sess) {
sess.cookie.expires = 'string' == typeof sess.cookie.expires ? new Date(sess.cookie.expires) : sess.cookie.expires;
if (!sess.cookie.expires || new Date() < expires) {
fn(null, sess);
} else {
self.destroy(sid, fn);
}
} else {
fn();
}
});
};
SessionStore.prototype.set = function(sid, sess, fn){
messageLogger.debug('SET ' + sid);
db.set("sessionstorage:" + sid, sess);
process.nextTick(function(){
if(fn) fn();
});
};
SessionStore.prototype.destroy = function(sid, fn){
messageLogger.debug('DESTROY ' + sid);
db.remove("sessionstorage:" + sid);
process.nextTick(function(){
if(fn) fn();
});
};
SessionStore.prototype.all = function(fn){
messageLogger.debug('ALL');
var sessions = [];
db.forEach(function(key, value){
if (key.substr(0,15) === "sessionstorage:") {
sessions.push(value);
}
});
fn(null, sessions);
};
SessionStore.prototype.clear = function(fn){
messageLogger.debug('CLEAR');
db.forEach(function(key, value){
if (key.substr(0,15) === "sessionstorage:") {
db.db.remove("session:" + key);
}
});
if(fn) fn();
};
SessionStore.prototype.length = function(fn){
messageLogger.debug('LENGTH');
var i = 0;
db.forEach(function(key, value){
if (key.substr(0,15) === "sessionstorage:") {
i++;
}
});
fn(null, i);
};

View file

@ -216,6 +216,9 @@ var version =
} }
}; };
// set the latest available API version here
exports.latestApiVersion = '1.2.7';
/** /**
* Handles a HTTP API call * Handles a HTTP API call
* @param functionName the name of the called function * @param functionName the name of the called function

View file

@ -20,6 +20,7 @@
var ERR = require("async-stacktrace"); var ERR = require("async-stacktrace");
var exporthtml = require("../utils/ExportHtml"); var exporthtml = require("../utils/ExportHtml");
var exporttxt = require("../utils/ExportTxt");
var exportdokuwiki = require("../utils/ExportDokuWiki"); var exportdokuwiki = require("../utils/ExportDokuWiki");
var padManager = require("../db/PadManager"); var padManager = require("../db/PadManager");
var async = require("async"); var async = require("async");
@ -48,22 +49,75 @@ exports.doExport = function(req, res, padId, type)
res.attachment(padId + "." + type); res.attachment(padId + "." + type);
//if this is a plain text export, we can do this directly //if this is a plain text export, we can do this directly
// We have to over engineer this because tabs are stored as attributes and not plain text
if(type == "txt") if(type == "txt")
{ {
padManager.getPad(padId, function(err, pad) var txt;
var randNum;
var srcFile, destFile;
async.series([
//render the txt document
function(callback)
{ {
ERR(err); exporttxt.getPadTXTDocument(padId, req.params.rev, false, function(err, _txt)
if(req.params.rev){
pad.getInternalRevisionAText(req.params.rev, function(junk, text)
{ {
res.send(text.text ? text.text : null); 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 else
{ {
res.send(pad.text()); fs.unlink(destFile, callback);
} }
}); }
], callback);
}
], function(err)
{
if(err && err != "stop") ERR(err);
})
} }
else if(type == 'dokuwiki') else if(type == 'dokuwiki')
{ {

View file

@ -32,14 +32,10 @@ var securityManager = require("../db/SecurityManager");
var plugins = require("ep_etherpad-lite/static/js/pluginfw/plugins.js"); var plugins = require("ep_etherpad-lite/static/js/pluginfw/plugins.js");
var log4js = require('log4js'); var log4js = require('log4js');
var messageLogger = log4js.getLogger("message"); var messageLogger = log4js.getLogger("message");
var accessLogger = log4js.getLogger("access");
var _ = require('underscore'); var _ = require('underscore');
var hooks = require("ep_etherpad-lite/static/js/pluginfw/hooks.js"); var hooks = require("ep_etherpad-lite/static/js/pluginfw/hooks.js");
/**
* A associative array that saves which sessions belong to a pad
*/
var pad2sessions = {};
/** /**
* A associative array that saves informations about a session * A associative array that saves informations about a session
* key = sessionId * key = sessionId
@ -83,14 +79,11 @@ exports.handleConnect = function(client)
exports.kickSessionsFromPad = function(padID) exports.kickSessionsFromPad = function(padID)
{ {
//skip if there is nobody on this pad //skip if there is nobody on this pad
if(!pad2sessions[padID]) if(socketio.sockets.clients(padID).length == 0)
return; return;
//disconnect everyone from this pad //disconnect everyone from this pad
for(var i in pad2sessions[padID]) socketio.sockets.in(padID).json.send({disconnect:"deleted"});
{
socketio.sockets.sockets[pad2sessions[padID][i]].json.send({disconnect:"deleted"});
}
} }
/** /**
@ -100,15 +93,13 @@ exports.kickSessionsFromPad = function(padID)
exports.handleDisconnect = function(client) exports.handleDisconnect = function(client)
{ {
//save the padname of this session //save the padname of this session
var sessionPad=sessioninfos[client.id].padId; var session = sessioninfos[client.id];
//if this connection was already etablished with a handshake, send a disconnect message to the others //if this connection was already etablished with a handshake, send a disconnect message to the others
if(sessioninfos[client.id] && sessioninfos[client.id].author) if(session && session.author)
{ {
var author = sessioninfos[client.id].author;
//get the author color out of the db //get the author color out of the db
authorManager.getAuthorColorId(author, function(err, color) authorManager.getAuthorColorId(session.author, function(err, color)
{ {
ERR(err); ERR(err);
@ -121,32 +112,19 @@ exports.handleDisconnect = function(client)
"ip": "127.0.0.1", "ip": "127.0.0.1",
"colorId": color, "colorId": color,
"userAgent": "Anonymous", "userAgent": "Anonymous",
"userId": author "userId": session.author
} }
} }
}; };
//Go trough all user that are still on the pad, and send them the USER_LEAVE message //Go trough all user that are still on the pad, and send them the USER_LEAVE message
for(i in pad2sessions[sessionPad]) client.broadcast.to(session.padId).json.send(messageToTheOtherUsers);
{
var socket = socketio.sockets.sockets[pad2sessions[sessionPad][i]];
if(socket !== undefined){
socket.json.send(messageToTheOtherUsers);
}
}
}); });
} }
//Go trough all sessions of this pad, search and destroy the entry of this client client.get('remoteAddress', function(er, ip) {
for(i in pad2sessions[sessionPad]) accessLogger.info('[LEAVE] Pad "'+session.padId+'": Author "'+session.author+'" on client '+client.id+' with IP "'+ip+'" left the pad')
{ })
if(pad2sessions[sessionPad][i] == client.id)
{
pad2sessions[sessionPad].splice(i, 1);
break;
}
}
//Delete the sessioninfos entrys of this session //Delete the sessioninfos entrys of this session
delete sessioninfos[client.id]; delete sessioninfos[client.id];
@ -228,11 +206,10 @@ exports.handleMessage = function(client, message)
function(callback) function(callback)
{ {
if(!message.padId){
// If the message has a padId we assume the client is already known to the server and needs no re-authorization // If the message has a padId we assume the client is already known to the server and needs no re-authorization
callback(); if(!message.padId)
return; return callback();
}
// Note: message.sessionID is an entirely different kind of // Note: message.sessionID is an entirely different kind of
// session from the sessions we use here! Beware! FIXME: Call // session from the sessions we use here! Beware! FIXME: Call
// our "sessions" "connections". // our "sessions" "connections".
@ -292,9 +269,7 @@ exports.handleCustomMessage = function (padID, msg, cb) {
time: time time: time
} }
}; };
for (var i in pad2sessions[padID]) { socketio.sockets.in(padID).json.send(msg);
socketio.sockets.sockets[pad2sessions[padID][i]].json.send(msg);
}
cb(null, {}); cb(null, {});
} }
@ -352,10 +327,7 @@ function handleChatMessage(client, message)
}; };
//broadcast the chat message to everyone on the pad //broadcast the chat message to everyone on the pad
for(var i in pad2sessions[padId]) socketio.sockets.in(padId).json.send(msg);
{
socketio.sockets.sockets[pad2sessions[padId][i]].json.send(msg);
}
callback(); callback();
} }
@ -422,14 +394,7 @@ function handleGetChatMessages(client, message)
}; };
// send the messages back to the client // send the messages back to the client
for(var i in pad2sessions[padId]) client.json.send(infoMsg);
{
if(pad2sessions[padId][i] == client.id)
{
socketio.sockets.sockets[pad2sessions[padId][i]].json.send(infoMsg);
break;
}
}
}); });
}]); }]);
} }
@ -453,14 +418,14 @@ function handleSuggestUserName(client, message)
return; return;
} }
var padId = sessioninfos[client.id].padId; var padId = sessioninfos[client.id].padId,
clients = socketio.sockets.clients(padId);
//search the author and send him this message //search the author and send him this message
for(var i in pad2sessions[padId]) for(var i = 0; i < clients.length; i++) {
{ var session = sessioninfos[clients[i].id];
if(sessioninfos[pad2sessions[padId][i]].author == message.data.payload.unnamedId) if(session && session.author == message.data.payload.unnamedId) {
{ clients[i].json.send(message);
socketio.sockets.sockets[pad2sessions[padId][i]].send(message);
break; break;
} }
} }
@ -501,7 +466,8 @@ function handleUserInfoUpdate(client, message)
type: "USER_NEWINFO", type: "USER_NEWINFO",
userInfo: { userInfo: {
userId: author, userId: author,
name: message.data.userInfo.name, //set a null name, when there is no name set. cause the client wants it null
name: message.data.userInfo.name || null,
colorId: message.data.userInfo.colorId, colorId: message.data.userInfo.colorId,
userAgent: "Anonymous", userAgent: "Anonymous",
ip: "127.0.0.1", ip: "127.0.0.1",
@ -509,20 +475,8 @@ function handleUserInfoUpdate(client, message)
} }
}; };
//set a null name, when there is no name set. cause the client wants it null
if(infoMsg.data.userInfo.name == null)
{
infoMsg.data.userInfo.name = null;
}
//Send the other clients on the pad the update message //Send the other clients on the pad the update message
for(var i in pad2sessions[padId]) client.broadcast.to(padId).json.send(infoMsg);
{
if(pad2sessions[padId][i] != client.id)
{
socketio.sockets.sockets[pad2sessions[padId][i]].json.send(infoMsg);
}
}
} }
/** /**
@ -632,7 +586,14 @@ function handleUserChanges(client, message)
// client) are relative to revision r - 1. The follow function // client) are relative to revision r - 1. The follow function
// rebases "changeset" so that it is relative to revision r // rebases "changeset" so that it is relative to revision r
// and can be applied after "c". // and can be applied after "c".
try
{
changeset = Changeset.follow(c, changeset, false, apool); changeset = Changeset.follow(c, changeset, false, apool);
}catch(e){
console.warn("Can't apply USER_CHANGES "+changeset+", possibly because of mismatched follow error");
client.json.send({disconnect:"badChangeset"});
return;
}
if ((r - baseRev) % 200 == 0) { // don't let the stack get too deep if ((r - baseRev) % 200 == 0) { // don't let the stack get too deep
async.nextTick(callback); async.nextTick(callback);
@ -682,65 +643,53 @@ function handleUserChanges(client, message)
exports.updatePadClients = function(pad, callback) exports.updatePadClients = function(pad, callback)
{ {
//skip this step if noone is on this pad //skip this step if noone is on this pad
if(!pad2sessions[pad.id]) var roomClients = socketio.sockets.clients(pad.id);
{ if(roomClients.length==0)
callback(); return callback();
return;
} // since all clients usually get the same set of changesets, store them in local cache
// to remove unnecessary roundtrip to the datalayer
// TODO: in REAL world, if we're working without datalayer cache, all requests to revisions will be fired
// BEFORE first result will be landed to our cache object. The solution is to replace parallel processing
// via async.forEach with sequential for() loop. There is no real benefits of running this in parallel,
// but benefit of reusing cached revision object is HUGE
var revCache = {};
//go trough all sessions on this pad //go trough all sessions on this pad
async.forEach(pad2sessions[pad.id], function(session, callback) async.forEach(roomClients, function(client, callback)
{ {
var sid = client.id;
//https://github.com/caolan/async#whilst //https://github.com/caolan/async#whilst
//send them all new changesets //send them all new changesets
async.whilst( async.whilst(
function (){ return sessioninfos[session] && sessioninfos[session].rev < pad.getHeadRevisionNumber()}, function (){ return sessioninfos[sid] && sessioninfos[sid].rev < pad.getHeadRevisionNumber()},
function(callback) function(callback)
{ {
var author, revChangeset, currentTime; var r = sessioninfos[sid].rev + 1;
var r = sessioninfos[session].rev + 1;
async.parallel([ async.waterfall([
function (callback) function(callback) {
{ if(revCache[r])
pad.getRevisionAuthor(r, function(err, value) callback(null, revCache[r]);
{ else
if(ERR(err, callback)) return; pad.getRevision(r, callback);
author = value;
callback();
});
}, },
function (callback) function(revision, callback)
{ {
pad.getRevisionChangeset(r, function(err, value) revCache[r] = revision;
{
if(ERR(err, callback)) return; var author = revision.meta.author,
revChangeset = value; revChangeset = revision.changeset,
callback(); currentTime = revision.meta.timestamp;
});
},
function (callback)
{
pad.getRevisionDate(r, function(err, date)
{
if(ERR(err, callback)) return;
currentTime = date;
callback();
});
}
], function(err)
{
if(ERR(err, callback)) return;
// next if session has not been deleted // next if session has not been deleted
if(sessioninfos[session] == null) if(sessioninfos[sid] == null)
return callback(null);
if(author == sessioninfos[sid].author)
{ {
callback(null); client.json.send({"type":"COLLABROOM","data":{type:"ACCEPT_COMMIT", newRev:r}});
return;
}
if(author == sessioninfos[session].author)
{
socketio.sockets.sockets[session].json.send({"type":"COLLABROOM","data":{type:"ACCEPT_COMMIT", newRev:r}});
} }
else else
{ {
@ -752,20 +701,18 @@ exports.updatePadClients = function(pad, callback)
apool: forWire.pool, apool: forWire.pool,
author: author, author: author,
currentTime: currentTime, currentTime: currentTime,
timeDelta: currentTime - sessioninfos[session].time timeDelta: currentTime - sessioninfos[sid].time
}}; }};
socketio.sockets.sockets[session].json.send(wireMsg); client.json.send(wireMsg);
} }
if(sessioninfos[session] != null) sessioninfos[sid].time = currentTime;
{ sessioninfos[sid].rev = r;
sessioninfos[session].time = currentTime;
sessioninfos[session].rev = r;
}
callback(null); callback(null);
}); }
], callback);
}, },
callback callback
); );
@ -895,23 +842,14 @@ function handleClientReady(client, message)
function(callback) function(callback)
{ {
async.parallel([ async.parallel([
//get colorId //get colorId and name
function(callback) function(callback)
{ {
authorManager.getAuthorColorId(author, function(err, value) authorManager.getAuthor(author, function(err, value)
{ {
if(ERR(err, callback)) return; if(ERR(err, callback)) return;
authorColorId = value; authorColorId = value.colorId;
callback(); authorName = value.name;
});
},
//get author name
function(callback)
{
authorManager.getAuthorName(author, function(err, value)
{
if(ERR(err, callback)) return;
authorName = value;
callback(); callback();
}); });
}, },
@ -965,21 +903,17 @@ function handleClientReady(client, message)
{ {
//Check that the client is still here. It might have disconnected between callbacks. //Check that the client is still here. It might have disconnected between callbacks.
if(sessioninfos[client.id] === undefined) if(sessioninfos[client.id] === undefined)
{ return callback();
callback();
return;
}
//Check if this author is already on the pad, if yes, kick the other sessions! //Check if this author is already on the pad, if yes, kick the other sessions!
if(pad2sessions[padIds.padId]) var roomClients = socketio.sockets.clients(padIds.padId);
{ for(var i = 0; i < roomClients.length; i++) {
for(var i in pad2sessions[padIds.padId]) var sinfo = sessioninfos[roomClients[i].id];
{ if(sinfo && sinfo.author == author) {
if(sessioninfos[pad2sessions[padIds.padId][i]] && sessioninfos[pad2sessions[padIds.padId][i]].author == author) // fix user's counter, works on page refresh or if user closes browser window and then rejoins
{ sessioninfos[roomClients[i].id] = {};
var socket = socketio.sockets.sockets[pad2sessions[padIds.padId][i]]; roomClients[i].leave(padIds.padId);
if(socket) socket.json.send({disconnect:"userdup"}); roomClients[i].json.send({disconnect:"userdup"});
}
} }
} }
@ -988,18 +922,21 @@ function handleClientReady(client, message)
sessioninfos[client.id].readOnlyPadId = padIds.readOnlyPadId; sessioninfos[client.id].readOnlyPadId = padIds.readOnlyPadId;
sessioninfos[client.id].readonly = padIds.readonly; sessioninfos[client.id].readonly = padIds.readonly;
//check if there is already a pad2sessions entry, if not, create one //Log creation/(re-)entering of a pad
if(!pad2sessions[padIds.padId]) client.get('remoteAddress', function(er, ip) {
{ if(pad.head > 0) {
pad2sessions[padIds.padId] = []; accessLogger.info('[ENTER] Pad "'+padIds.padId+'": Client '+client.id+' with IP "'+ip+'" entered the pad');
} }
else if(pad.head == 0) {
//Saves in pad2sessions that this session belongs to this pad accessLogger.info('[CREATE] Pad "'+padIds.padId+'": Client '+client.id+' with IP "'+ip+'" created the pad');
pad2sessions[padIds.padId].push(client.id); }
})
//If this is a reconnect, we don't have to send the client the ClientVars again //If this is a reconnect, we don't have to send the client the ClientVars again
if(message.reconnect == true) if(message.reconnect == true)
{ {
//Join the pad and start receiving updates
client.join(padIds.padId);
//Save the revision in sessioninfos, we take the revision from the info the client send to us //Save the revision in sessioninfos, we take the revision from the info the client send to us
sessioninfos[client.id].rev = message.client_rev; sessioninfos[client.id].rev = message.client_rev;
} }
@ -1044,17 +981,12 @@ function handleClientReady(client, message)
// tell the client the number of the latest chat-message, which will be // tell the client the number of the latest chat-message, which will be
// used to request the latest 100 chat-messages later (GET_CHAT_MESSAGES) // used to request the latest 100 chat-messages later (GET_CHAT_MESSAGES)
"chatHead": pad.chatHead, "chatHead": pad.chatHead,
"numConnectedUsers": pad2sessions[padIds.padId].length, "numConnectedUsers": roomClients.length,
"isProPad": false,
"readOnlyId": padIds.readOnlyPadId, "readOnlyId": padIds.readOnlyPadId,
"readonly": padIds.readonly, "readonly": padIds.readonly,
"serverTimestamp": new Date().getTime(), "serverTimestamp": new Date().getTime(),
"globalPadId": message.padId, "globalPadId": message.padId,
"userId": author, "userId": author,
"cookiePrefsToSet": {
"fullWidth": false,
"hideSidebar": false
},
"abiwordAvailable": settings.abiwordAvailable(), "abiwordAvailable": settings.abiwordAvailable(),
"plugins": { "plugins": {
"plugins": plugins.plugins, "plugins": plugins.plugins,
@ -1080,6 +1012,8 @@ function handleClientReady(client, message)
} }
}); });
//Join the pad and start receiving updates
client.join(padIds.padId);
//Send the clientVars to the Client //Send the clientVars to the Client
client.json.send({type: "CLIENT_VARS", data: clientVars}); client.json.send({type: "CLIENT_VARS", data: clientVars});
//Save the current revision in sessioninfos, should be the same as in clientVars //Save the current revision in sessioninfos, should be the same as in clientVars
@ -1109,71 +1043,53 @@ function handleClientReady(client, message)
messageToTheOtherUsers.data.userInfo.name = authorName; messageToTheOtherUsers.data.userInfo.name = authorName;
} }
// notify all existing users about new user
client.broadcast.to(padIds.padId).json.send(messageToTheOtherUsers);
//Run trough all sessions of this pad //Run trough all sessions of this pad
async.forEach(pad2sessions[padIds.padId], function(sessionID, callback) async.forEach(socketio.sockets.clients(padIds.padId), function(roomClient, callback)
{ {
var author, socket, sessionAuthorName, sessionAuthorColorId; var author;
//Jump over, if this session is the connection session
if(roomClient.id == client.id)
return callback();
//Since sessioninfos might change while being enumerated, check if the //Since sessioninfos might change while being enumerated, check if the
//sessionID is still assigned to a valid session //sessionID is still assigned to a valid session
if(sessioninfos[sessionID] !== undefined && if(sessioninfos[roomClient.id] !== undefined)
socketio.sockets.sockets[sessionID] !== undefined){ author = sessioninfos[roomClient.id].author;
author = sessioninfos[sessionID].author; else // If the client id is not valid, callback();
socket = socketio.sockets.sockets[sessionID]; return callback();
}else {
// If the sessionID is not valid, callback(); async.waterfall([
callback();
return;
}
async.series([
//get the authorname & colorId //get the authorname & colorId
function(callback) function(callback)
{ {
async.parallel([ // reuse previously created cache of author's data
function(callback) if(historicalAuthorData[author])
{ callback(null, historicalAuthorData[author]);
authorManager.getAuthorColorId(author, function(err, value) else
{ authorManager.getAuthor(author, callback);
if(ERR(err, callback)) return;
sessionAuthorColorId = value;
callback();
})
}, },
function(callback) function (authorInfo, callback)
{ {
authorManager.getAuthorName(author, function(err, value)
{
if(ERR(err, callback)) return;
sessionAuthorName = value;
callback();
})
}
],callback);
},
function (callback)
{
//Jump over, if this session is the connection session
if(sessionID != client.id)
{
//Send this Session the Notification about the new user
socket.json.send(messageToTheOtherUsers);
//Send the new User a Notification about this other user //Send the new User a Notification about this other user
var messageToNotifyTheClientAboutTheOthers = { var msg = {
"type": "COLLABROOM", "type": "COLLABROOM",
"data": { "data": {
type: "USER_NEWINFO", type: "USER_NEWINFO",
userInfo: { userInfo: {
"ip": "127.0.0.1", "ip": "127.0.0.1",
"colorId": sessionAuthorColorId, "colorId": authorInfo.colorId,
"name": sessionAuthorName, "name": authorInfo.name,
"userAgent": "Anonymous", "userAgent": "Anonymous",
"userId": author "userId": author
} }
} }
}; };
client.json.send(messageToNotifyTheClientAboutTheOthers); client.json.send(msg);
}
} }
], callback); ], callback);
}, callback); }, callback);
@ -1521,33 +1437,30 @@ function composePadChangesets(padId, startNum, endNum, callback)
* Get the number of users in a pad * Get the number of users in a pad
*/ */
exports.padUsersCount = function (padID, callback) { exports.padUsersCount = function (padID, callback) {
if (!pad2sessions[padID] || typeof pad2sessions[padID] != typeof []) { callback(null, {
callback(null, {padUsersCount: 0}); padUsersCount: socketio.sockets.clients(padID).length
} else { });
callback(null, {padUsersCount: pad2sessions[padID].length});
}
} }
/** /**
* Get the list of users in a pad * Get the list of users in a pad
*/ */
exports.padUsers = function (padID, callback) { exports.padUsers = function (padID, callback) {
if (!pad2sessions[padID] || typeof pad2sessions[padID] != typeof []) { var result = [];
callback(null, {padUsers: []});
} else { async.forEach(socketio.sockets.clients(padId), function(roomClient, callback) {
var authors = []; var s = sessioninfos[roomClient.id];
for ( var ix in sessioninfos ) { if(s) {
if ( sessioninfos[ix].padId !== padID ) { authorManager.getAuthor(s.author, function(err, author) {
continue; if(ERR(err, callback)) return;
}
var aid = sessioninfos[ix].author; author.id = s.author;
authorManager.getAuthor( aid, function ( err, author ) { result.push(author);
author.id = aid;
authors.push( author );
if ( authors.length === pad2sessions[padID].length ) {
callback(null, {padUsers: authors});
}
}); });
} }
} }, function(err) {
if(ERR(err, callback)) return;
callback(null, {padUsers: result});
});
} }

View file

@ -55,13 +55,14 @@ exports.setSocketIO = function(_socket)
socket.sockets.on('connection', function(client) socket.sockets.on('connection', function(client)
{ {
client.set('remoteAddress', client.handshake.address.address);
var clientAuthorized = false; var clientAuthorized = false;
//wrap the original send function to log the messages //wrap the original send function to log the messages
client._send = client.send; client._send = client.send;
client.send = function(message) client.send = function(message)
{ {
messageLogger.info("to " + client.id + ": " + stringifyWithoutPassword(message)); messageLogger.debug("to " + client.id + ": " + stringifyWithoutPassword(message));
client._send(message); client._send(message);
} }
@ -79,7 +80,7 @@ exports.setSocketIO = function(_socket)
//check if component is registered in the components array //check if component is registered in the components array
if(components[message.component]) if(components[message.component])
{ {
messageLogger.info("from " + client.id + ": " + stringifyWithoutPassword(message)); messageLogger.debug("from " + client.id + ": " + stringifyWithoutPassword(message));
components[message.component].handleMessage(client, message); components[message.component].handleMessage(client, message);
} }
} }

View file

@ -57,4 +57,9 @@ exports.expressCreateServer = function (hook_name, args, cb) {
res.end("OK"); res.end("OK");
}); });
}); });
//Provide a possibility to query the latest available API version
args.app.get('/api', function (req, res) {
res.json({"currentVersion" : apiHandler.latestApiVersion});
});
} }

View file

@ -1,14 +1,26 @@
var path = require("path") var path = require("path")
, npm = require("npm") , npm = require("npm")
, fs = require("fs"); , fs = require("fs")
, async = require("async");
exports.expressCreateServer = function (hook_name, args, cb) { exports.expressCreateServer = function (hook_name, args, cb) {
args.app.get('/tests/frontend/specs_list.js', function(req, res){ args.app.get('/tests/frontend/specs_list.js', function(req, res){
fs.readdir('tests/frontend/specs', function(err, files){
if(err){ return res.send(500); }
async.parallel({
coreSpecs: function(callback){
exports.getCoreTests(callback);
},
pluginSpecs: function(callback){
exports.getPluginTests(callback);
}
},
function(err, results){
var files = results.coreSpecs; // push the core specs to a file object
files = files.concat(results.pluginSpecs); // add the plugin Specs to the core specs
console.debug("Sent browser the following test specs:", files.sort());
res.send("var specs_list = " + JSON.stringify(files.sort()) + ";\n"); res.send("var specs_list = " + JSON.stringify(files.sort()) + ";\n");
}); });
}); });
var url2FilePath = function(url){ var url2FilePath = function(url){
@ -45,3 +57,28 @@ exports.expressCreateServer = function (hook_name, args, cb) {
res.redirect('/tests/frontend/'); res.redirect('/tests/frontend/');
}); });
} }
exports.getPluginTests = function(callback){
var pluginSpecs = [];
var plugins = fs.readdirSync('node_modules');
plugins.forEach(function(plugin){
if(fs.existsSync("node_modules/"+plugin+"/static/tests/frontend/specs")){ // if plugins exists
var specFiles = fs.readdirSync("node_modules/"+plugin+"/static/tests/frontend/specs/");
async.forEach(specFiles, function(spec){ // for each specFile push it to pluginSpecs
pluginSpecs.push("/static/plugins/"+plugin+"/static/tests/frontend/specs/" + spec);
},
function(err){
// blow up if something bad happens!
});
}
});
callback(null, pluginSpecs);
}
exports.getCoreTests = function(callback){
fs.readdir('tests/frontend/specs', function(err, coreSpecs){ // get the core test specs
if(err){ return res.send(500); }
callback(null, coreSpecs);
});
}

View file

@ -4,7 +4,7 @@ var httpLogger = log4js.getLogger("http");
var settings = require('../../utils/Settings'); var settings = require('../../utils/Settings');
var randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString; var randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString;
var hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks'); var hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks');
var ueberStore = require('../../db/SessionStore');
//checks for basic http auth //checks for basic http auth
exports.basicAuth = function (req, res, next) { exports.basicAuth = function (req, res, next) {
@ -102,15 +102,14 @@ exports.expressConfigure = function (hook_name, args, cb) {
* handling it cleaner :) */ * handling it cleaner :) */
if (!exports.sessionStore) { if (!exports.sessionStore) {
exports.sessionStore = new express.session.MemoryStore(); exports.sessionStore = new ueberStore();
exports.secret = randomString(32); exports.secret = settings.sessionKey; // Isn't this being reset each time the server spawns?
} }
args.app.use(express.cookieParser(exports.secret)); args.app.use(express.cookieParser(exports.secret));
args.app.sessionStore = exports.sessionStore; args.app.sessionStore = exports.sessionStore;
args.app.use(express.session({store: args.app.sessionStore, args.app.use(express.session({secret: exports.secret, store: args.app.sessionStore, key: 'express_sid' }));
key: 'express_sid' }));
args.app.use(exports.basicAuth); args.app.use(exports.basicAuth);
} }

View file

@ -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) + ";"
});
}

View file

@ -21,31 +21,9 @@ var padManager = require("../db/PadManager");
var ERR = require("async-stacktrace"); var ERR = require("async-stacktrace");
var Security = require('ep_etherpad-lite/static/js/security'); var Security = require('ep_etherpad-lite/static/js/security');
var hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks'); var hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks');
function getPadPlainText(pad, revNum) var getPadPlainText = require('./ExportHelper').getPadPlainText
{ var _analyzeLine = require('./ExportHelper')._analyzeLine;
var atext = ((revNum !== undefined) ? pad.getInternalRevisionAText(revNum) : pad.atext()); var _encodeWhitespace = require('./ExportHelper')._encodeWhitespace;
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('');
}
function getPadHTML(pad, revNum, callback) function getPadHTML(pad, revNum, callback)
{ {
@ -503,45 +481,6 @@ function getHTMLFromAtext(pad, atext, authorColors)
return pieces.join(''); 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) exports.getPadHTMLDocument = function (padId, revNum, noDocType, callback)
{ {
padManager.getPad(padId, function (err, pad) 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, '&nbsp;');
}
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] = '&nbsp;';
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] = '&nbsp;';
break;
}
else if (p.charAt(0) != "<")
{
break;
}
}
}
else
{
for (var i = 0; i < parts.length; i++)
{
var p = parts[i];
if (p == " ")
{
parts[i] = '&nbsp;';
}
}
}
return parts.join('');
}
// copied from ACE // copied from ACE
var _REGEX_WORDCHAR = /[\u0030-\u0039\u0041-\u005A\u0061-\u007A\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\u1FFF\u3040-\u9FFF\uF900-\uFDFF\uFE70-\uFEFE\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFDC]/; var _REGEX_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; return urls;
} }
// copied from ACE
function _processSpaces(s){
var doesWrap = true;
if (s.indexOf("<") < 0 && !doesWrap){
// short-cut
return s.replace(/ /g, '&nbsp;');
}
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] = '&nbsp;';
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] = '&nbsp;';
break;
}
else if (p.charAt(0) != "<"){
break;
}
}
}
else
{
for (var i = 0; i < parts.length; i++){
var p = parts[i];
if (p == " "){
parts[i] = '&nbsp;';
}
}
}
return parts.join('');
}

293
src/node/utils/ExportTxt.js Normal file
View file

@ -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,
// <b>Just bold<b> <b><i>Bold and italics</i></b> <i>Just italics</i>
// becomes
// <b>Just bold <i>Bold and italics</i></b> <i>Just italics</i>
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 <ol><li>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);
});
});
}

View file

@ -26,6 +26,8 @@ var argv = require('./Cli').argv;
var npm = require("npm/lib/npm.js"); var npm = require("npm/lib/npm.js");
var vm = require('vm'); var vm = require('vm');
var log4js = require("log4js"); var log4js = require("log4js");
var randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString;
/* Root path of the installation */ /* Root path of the installation */
exports.root = path.normalize(path.join(npm.dir, "..")); exports.root = path.normalize(path.join(npm.dir, ".."));
@ -112,6 +114,11 @@ exports.loglevel = "INFO";
*/ */
exports.logconfig = { appenders: [{ type: "console" }]}; exports.logconfig = { appenders: [{ type: "console" }]};
/*
* Session Key, do not sure this.
*/
exports.sessionKey = false;
/* This setting is used if you need authentication and/or /* This setting is used if you need authentication and/or
* authorization. Note: /admin always requires authentication, and * authorization. Note: /admin always requires authentication, and
* either authorization by a module, or a user with is_admin set */ * either authorization by a module, or a user with is_admin set */
@ -132,8 +139,6 @@ exports.abiwordAvailable = function()
} }
} }
exports.reloadSettings = function reloadSettings() { exports.reloadSettings = function reloadSettings() {
// Discover where the settings file lives // Discover where the settings file lives
var settingsFilename = argv.settings || "settings.json"; var settingsFilename = argv.settings || "settings.json";
@ -152,6 +157,7 @@ exports.reloadSettings = function reloadSettings() {
try { try {
if(settingsStr) { if(settingsStr) {
settings = vm.runInContext('exports = '+settingsStr, vm.createContext(), "settings.json"); settings = vm.runInContext('exports = '+settingsStr, vm.createContext(), "settings.json");
settings = JSON.parse(JSON.stringify(settings)) // fix objects having constructors of other vm.context
} }
}catch(e){ }catch(e){
console.error('There was an error processing your settings.json file: '+e.message); console.error('There was an error processing your settings.json file: '+e.message);
@ -184,6 +190,11 @@ exports.reloadSettings = function reloadSettings() {
log4js.setGlobalLogLevel(exports.loglevel);//set loglevel log4js.setGlobalLogLevel(exports.loglevel);//set loglevel
log4js.replaceConsole(); log4js.replaceConsole();
if(!exports.sessionKey){ // If the secretKey isn't set we also create yet another unique value here
exports.sessionKey = randomString(32);
console.warn("You need to set a sessionKey value in settings.json, this will allow your users to reconnect to your Etherpad Instance if your instance restarts");
}
if(exports.dbType === "dirty"){ if(exports.dbType === "dirty"){
console.warn("DirtyDB is used. This is fine for testing but not recommended for production.") console.warn("DirtyDB is used. This is fine for testing but not recommended for production.")
} }

View file

@ -16,7 +16,7 @@
"require-kernel" : "1.0.5", "require-kernel" : "1.0.5",
"resolve" : "0.2.x", "resolve" : "0.2.x",
"socket.io" : "0.9.x", "socket.io" : "0.9.x",
"ueberDB" : "0.1.9", "ueberDB" : "0.1.94",
"async" : "0.1.x", "async" : "0.1.x",
"express" : "3.x", "express" : "3.x",
"connect" : "2.4.x", "connect" : "2.4.x",
@ -40,11 +40,10 @@
}, },
"bin": { "etherpad-lite": "./node/server.js" }, "bin": { "etherpad-lite": "./node/server.js" },
"devDependencies": { "devDependencies": {
"jshint" : "*", "wd" : "0.0.31"
"wd" : "0.0.26"
}, },
"engines" : { "node" : ">=0.6.0", "engines" : { "node" : ">=0.6.3",
"npm" : ">=1.0" "npm" : ">=1.0"
}, },
"version" : "1.2.7" "version" : "1.2.8"
} }

View file

@ -1,65 +1,59 @@
html, body {
height: 100%;
box-sizing: border-box;
}
body { body {
margin: 0; margin: 0;
color: #333; color: #333;
font: 14px helvetica, sans-serif; font: 14px helvetica, sans-serif;
background: #ddd; background: #eee;
background: -webkit-radial-gradient(circle,#aaa,#eee 60%) center fixed;
background: -moz-radial-gradient(circle,#aaa,#eee 60%) center fixed;
background: -ms-radial-gradient(circle,#aaa,#eee 60%) center fixed;
background: -o-radial-gradient(circle,#aaa,#eee 60%) center fixed;
}
#topborder {
border-top: 8px solid rgba(51, 51, 51, 0.8);
position: fixed;
top: 0px;
width: 100%;
} }
div.menu { div.menu {
background: none repeat scroll 0% 0% rgba(255, 255, 255, 0.75);
box-shadow: 0px -4px 4px rgba(0, 0, 0, 0.3);
display: block;
float: left;
height: 100%; height: 100%;
padding: 15px; padding: 15px;
position: fixed;
width: 220px; width: 220px;
border-right: 1px solid #ccc;
position: fixed;
}
div.menu ul {
padding: 0;
} }
div.menu li { div.menu li {
list-style: none; list-style: none;
margin-left: 3px; margin-left: 3px;
line-height: 1.6 line-height: 3;
border-top: 1px solid #ccc;
}
div.menu li:last-child {
border-bottom: 1px solid #ccc;
} }
div.innerwrapper { div.innerwrapper {
display: block;
float: right;
opacity: 0.9;
padding: 15px; padding: 15px;
max-width: 860px; padding-left: 265px;
border-radius: 0 0 7px 7px;
margin-left:250px;
min-width:400px;
} }
#wrapper { #wrapper {
background: none repeat scroll 0px 0px #FFFFFF; background: none repeat scroll 0px 0px #FFFFFF;
box-shadow: 0px 1px 8px rgba(0, 0, 0, 0.3); box-shadow: 0px 1px 10px rgba(0, 0, 0, 0.2);
margin: auto; margin: auto;
max-width: 1150px; max-width: 1150px;
min-height: 100%; min-height: 100%;
overflow: auto;
padding-left: 15px;
opacity: .9;
} }
h1 { h1 {
font-size: 29px; font-size: 29px;
} }
h2 { h2 {
font-size: 24px; font-size: 24px;
} }
.separator { .separator {
margin: 10px 0; margin: 10px 0;
height: 1px; height: 1px;
@ -69,37 +63,45 @@ h2 {
background: -ms-linear-gradient(left, #fff, #aaa 20%, #aaa 80%, #fff); background: -ms-linear-gradient(left, #fff, #aaa 20%, #aaa 80%, #fff);
background: -o-linear-gradient(left, #fff, #aaa 20%, #aaa 80%, #fff); background: -o-linear-gradient(left, #fff, #aaa 20%, #aaa 80%, #fff);
} }
form { form {
margin-bottom: 0; margin-bottom: 0;
} }
#inner { #inner {
width: 300px; width: 300px;
margin: 0 auto; margin: 0 auto;
} }
input { input {
font-weight: bold; font-weight: bold;
font-size: 15px; font-size: 15px;
} }
input[type="button"] { input[type="button"] {
padding: 4px 6px; padding: 4px 6px;
margin: 0; margin: 0;
} }
table input[type="button"] { table input[type="button"] {
float: right; float: right;
width: 100px; width: 100px;
} }
input[type="text"] { input[type="text"] {
border-radius: 3px; border-radius: 3px;
box-sizing: border-box; box-sizing: border-box;
-moz-box-sizing: border-box; -moz-box-sizing: border-box;
padding: 10px; padding: 10px;
*padding: 0; /* IE7 hack */ *padding: 0;
/* IE7 hack */
width: 100%; width: 100%;
outline: none; outline: none;
border: 1px solid #ddd; border: 1px solid #ddd;
margin: 0 0 5px 0; margin: 0 0 5px 0;
max-width: 500px; max-width: 500px;
} }
table { table {
border: 1px solid #ddd; border: 1px solid #ddd;
border-radius: 3px; border-radius: 3px;
@ -107,24 +109,34 @@ table {
width: 100%; width: 100%;
margin: 20px 0; margin: 20px 0;
} }
table thead tr { table thead tr {
background: #eee; background: #eee;
} }
td, th { td, th {
padding: 5px; padding: 5px;
} }
.template { .template {
display: none; display: none;
} }
#progress { #progress {
position: absolute; position: absolute;
bottom: 50px; bottom: 50px;
} }
.settings {
margin-top:10px; #progress img {
width:100%; vertical-align: top;
min-height:600px;
} }
.settings {
outline: none;
width: 100%;
min-height: 500px;
}
#response { #response {
display: inline; display: inline;
} }
@ -132,9 +144,77 @@ td, th {
a:link, a:visited, a:hover, a:focus { a:link, a:visited, a:hover, a:focus {
color: #333333; color: #333333;
text-decoration: none; text-decoration: none;
border-bottom: #333333 1px dotted;
} }
a:focus, a:hover { a:focus, a:hover {
border-bottom: #333333 1px solid; border-bottom: #333333 1px solid;
} }
pre {
white-space: pre-wrap;
word-wrap: break-word;
}
@media (max-width: 720px) {
div.innerwrapper {
padding: 0 15px 15px 15px;
}
div.menu {
padding: 1px 15px 0 15px;
position: static;
height: auto;
border-right: none;
width: auto;
}
table {
border: none;
}
table, thead, tbody, td, tr {
display: block;
}
thead tr {
display: none;
}
tr {
border: 1px solid #ccc;
margin-bottom: 5px;
border-radius: 3px;
}
td {
border: none;
border-bottom: 1px solid #eee;
position: relative;
padding-left: 50%;
white-space: normal;
text-align: left;
}
td.name {
word-wrap: break-word;
}
td:before {
position: absolute;
top: 6px;
left: 6px;
text-align: left;
padding-right: 10px;
white-space: nowrap;
font-weight: bold;
content: attr(data-label);
}
td:last-child {
border-bottom: none;
}
table input[type="button"] {
float: none;
}
}

View file

@ -176,3 +176,11 @@ p {
} }
#overlaysdiv { position: absolute; left: -1000px; top: -1000px; } #overlaysdiv { position: absolute; left: -1000px; top: -1000px; }
/* Stops super long lines without being spaces such as aaaaaaaaaaaaaa*100 breaking the editor
Commented out because it stops IE from being able to render the document, crazy IE bug is crazy. */
/*
.ace-line{
overflow:hidden;
}
*/

View file

@ -33,19 +33,6 @@ function object(o)
f.prototype = o; f.prototype = o;
return new f(); return new f();
} }
var userAgent = (((function () {return this;})().navigator || {}).userAgent || 'node-js').toLowerCase();
// Figure out what browser is being used (stolen from jquery 1.2.1)
var browser = {
version: (userAgent.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/) || [])[1],
safari: /webkit/.test(userAgent),
opera: /opera/.test(userAgent),
msie: /msie/.test(userAgent) && !/opera/.test(userAgent),
mozilla: /mozilla/.test(userAgent) && !/(compatible|webkit)/.test(userAgent),
windows: /windows/.test(userAgent),
mobile: /mobile/.test(userAgent) || /android/.test(userAgent)
};
function getAssoc(obj, name) function getAssoc(obj, name)
{ {
@ -97,7 +84,6 @@ var noop = function(){};
exports.isNodeText = isNodeText; exports.isNodeText = isNodeText;
exports.object = object; exports.object = object;
exports.browser = browser;
exports.getAssoc = getAssoc; exports.getAssoc = getAssoc;
exports.setAssoc = setAssoc; exports.setAssoc = setAssoc;
exports.binarySearch = binarySearch; exports.binarySearch = binarySearch;

View file

@ -28,7 +28,7 @@ $ = jQuery = require('./rjquery').$;
_ = require("./underscore"); _ = require("./underscore");
var isNodeText = Ace2Common.isNodeText, var isNodeText = Ace2Common.isNodeText,
browser = Ace2Common.browser, browser = $.browser,
getAssoc = Ace2Common.getAssoc, getAssoc = Ace2Common.getAssoc,
setAssoc = Ace2Common.setAssoc, setAssoc = Ace2Common.setAssoc,
isTextNode = Ace2Common.isTextNode, isTextNode = Ace2Common.isTextNode,
@ -154,7 +154,8 @@ function Ace2Inner(){
var dmesg = noop; var dmesg = noop;
window.dmesg = noop; window.dmesg = noop;
var scheduler = parent;
var scheduler = parent; // hack for opera required
var textFace = 'monospace'; var textFace = 'monospace';
var textSize = 12; var textSize = 12;
@ -1621,9 +1622,17 @@ function Ace2Inner(){
lines = ccData.lines; lines = ccData.lines;
var lineAttribs = ccData.lineAttribs; var lineAttribs = ccData.lineAttribs;
var linesWrapped = ccData.linesWrapped; var linesWrapped = ccData.linesWrapped;
var scrollToTheLeftNeeded = false;
if (linesWrapped > 0) 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."); // console.log("Editor warning: " + linesWrapped + " long line" + (linesWrapped == 1 ? " was" : "s were") + " hard-wrapped into " + ccData.numLinesAfter + " lines.");
} }
@ -1691,6 +1700,10 @@ function Ace2Inner(){
//console.log("removed: "+id); //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"); p.mark("findsel");
// if the nodes that define the selection weren't encountered during // if the nodes that define the selection weren't encountered during
// content collection, figure out where those nodes are now. // content collection, figure out where those nodes are now.
@ -1896,7 +1909,7 @@ function Ace2Inner(){
var prevLine = rep.lines.prev(thisLine); var prevLine = rep.lines.prev(thisLine);
var prevLineText = prevLine.text; var prevLineText = prevLine.text;
var theIndent = /^ *(?:)/.exec(prevLineText)[0]; 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( var cs = Changeset.builder(rep.lines.totalWidth()).keep(
rep.lines.offsetOfIndex(lineNum), lineNum).insert( rep.lines.offsetOfIndex(lineNum), lineNum).insert(
theIndent, [ theIndent, [
@ -2817,7 +2830,6 @@ function Ace2Inner(){
rep.selStart = selectStart; rep.selStart = selectStart;
rep.selEnd = selectEnd; rep.selEnd = selectEnd;
rep.selFocusAtStart = newSelFocusAtStart; rep.selFocusAtStart = newSelFocusAtStart;
if (mozillaFakeArrows) mozillaFakeArrows.notifySelectionChanged();
currentCallStack.repChanged = true; currentCallStack.repChanged = true;
return true; return true;
@ -3317,8 +3329,10 @@ function Ace2Inner(){
function doIndentOutdent(isOut) function doIndentOutdent(isOut)
{ {
if (!(rep.selStart && rep.selEnd) || if (!((rep.selStart && rep.selEnd) ||
((rep.selStart[0] == rep.selEnd[0]) && (rep.selStart[1] == rep.selEnd[1]) && rep.selEnd[1] > 1)) ((rep.selStart[0] == rep.selEnd[0]) && (rep.selStart[1] == rep.selEnd[1]) && rep.selEnd[1] > 1)) &&
(isOut != true)
)
{ {
return false; return false;
} }
@ -3326,7 +3340,6 @@ function Ace2Inner(){
var firstLine, lastLine; var firstLine, lastLine;
firstLine = rep.selStart[0]; firstLine = rep.selStart[0];
lastLine = Math.max(firstLine, rep.selEnd[0] - ((rep.selEnd[1] === 0) ? 1 : 0)); lastLine = Math.max(firstLine, rep.selEnd[0] - ((rep.selEnd[1] === 0) ? 1 : 0));
var mods = []; var mods = [];
for (var n = firstLine; n <= lastLine; n++) for (var n = firstLine; n <= lastLine; n++)
{ {
@ -3539,7 +3552,6 @@ function Ace2Inner(){
{ {
// if (DEBUG && window.DONT_INCORP) return; // if (DEBUG && window.DONT_INCORP) return;
if (!isEditable) return; if (!isEditable) return;
var type = evt.type; var type = evt.type;
var charCode = evt.charCode; var charCode = evt.charCode;
var keyCode = evt.keyCode; var keyCode = evt.keyCode;
@ -3561,6 +3573,11 @@ function Ace2Inner(){
var isModKey = ((!charCode) && ((type == "keyup") || (type == "keydown")) && (keyCode == 16 || keyCode == 17 || keyCode == 18 || keyCode == 20 || keyCode == 224 || keyCode == 91)); var isModKey = ((!charCode) && ((type == "keyup") || (type == "keydown")) && (keyCode == 16 || keyCode == 17 || keyCode == 18 || keyCode == 20 || keyCode == 224 || keyCode == 91));
if (isModKey) return; if (isModKey) return;
// If the key is a keypress and the browser is opera and the key is enter, do nothign at all as this fires twice.
if (keyCode == 13 && browser.opera && (type == "keypress")){
return; // This stops double enters in Opera but double Tabs still show on single tab keypress, adding keyCode == 9 to this doesn't help as the event is fired twice
}
var specialHandled = false; var specialHandled = false;
var isTypeForSpecialKey = ((browser.msie || browser.safari) ? (type == "keydown") : (type == "keypress")); var isTypeForSpecialKey = ((browser.msie || browser.safari) ? (type == "keydown") : (type == "keypress"));
var isTypeForCmdKey = ((browser.msie || browser.safari) ? (type == "keydown") : (type == "keypress")); var isTypeForCmdKey = ((browser.msie || browser.safari) ? (type == "keydown") : (type == "keypress"));
@ -3690,12 +3707,73 @@ function Ace2Inner(){
doDeleteKey(); doDeleteKey();
specialHandled = true; specialHandled = true;
} }
if((evt.which == 33 || evt.which == 34) && type == 'keydown'){
if (mozillaFakeArrows && mozillaFakeArrows.handleKeyEvent(evt)) var oldVisibleLineRange = getVisibleLineRange();
{ var topOffset = rep.selStart[0] - oldVisibleLineRange[0];
evt.preventDefault(); if(topOffset < 0 ){
specialHandled = true; topOffset = 0;
} }
var isPageDown = evt.which === 34;
var isPageUp = evt.which === 33;
scheduler.setTimeout(function(){
var newVisibleLineRange = getVisibleLineRange();
var linesCount = rep.lines.length();
var newCaretRow = rep.selStart[0];
if(isPageUp){
newCaretRow = oldVisibleLineRange[0];
}
if(isPageDown){
newCaretRow = newVisibleLineRange[0] + topOffset;
}
//ensure min and max
if(newCaretRow < 0){
newCaretRow = 0;
}
if(newCaretRow >= linesCount){
newCaretRow = linesCount-1;
}
rep.selStart[0] = newCaretRow;
rep.selEnd[0] = newCaretRow;
updateBrowserSelectionFromRep();
}, 200);
}
/* Attempt to apply some sanity to cursor handling in Chrome after a copy / paste event
We have to do this the way we do because rep. doesn't hold the value for keyheld events IE if the user
presses and holds the arrow key */
if((evt.which == 37 || evt.which == 38 || evt.which == 39 || evt.which == 40) && $.browser.chrome){
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(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.
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
}
}
}
} }
if (type == "keydown") if (type == "keydown")
@ -3801,7 +3879,6 @@ function Ace2Inner(){
selection.endPoint = getPointForLineAndChar(se); selection.endPoint = getPointForLineAndChar(se);
selection.focusAtStart = !! rep.selFocusAtStart; selection.focusAtStart = !! rep.selFocusAtStart;
setSelection(selection); setSelection(selection);
} }
@ -4119,6 +4196,11 @@ function Ace2Inner(){
selection.startPoint = pointFromRangeBound(range.startContainer, range.startOffset); selection.startPoint = pointFromRangeBound(range.startContainer, range.startOffset);
selection.endPoint = pointFromRangeBound(range.endContainer, range.endOffset); selection.endPoint = pointFromRangeBound(range.endContainer, range.endOffset);
selection.focusAtStart = (((range.startContainer != range.endContainer) || (range.startOffset != range.endOffset)) && browserSelection.anchorNode && (browserSelection.anchorNode == range.endContainer) && (browserSelection.anchorOffset == range.endOffset)); selection.focusAtStart = (((range.startContainer != range.endContainer) || (range.startOffset != range.endOffset)) && browserSelection.anchorNode && (browserSelection.anchorNode == range.endContainer) && (browserSelection.anchorOffset == range.endOffset));
if(selection.startPoint.node.ownerDocument !== window.document){
return null;
}
return selection; return selection;
} }
else return null; else return null;
@ -5033,331 +5115,6 @@ function Ace2Inner(){
editorInfo.ace_doInsertUnorderedList = doInsertUnorderedList; editorInfo.ace_doInsertUnorderedList = doInsertUnorderedList;
editorInfo.ace_doInsertOrderedList = doInsertOrderedList; editorInfo.ace_doInsertOrderedList = doInsertOrderedList;
var mozillaFakeArrows = (browser.mozilla && (function()
{
// In Firefox 2, arrow keys are unstable while DOM-manipulating
// operations are going on. Specifically, if an operation
// (computation that ties up the event queue) is going on (in the
// call-stack of some event, like a timeout) that at some point
// mutates nodes involved in the selection, then the arrow
// keypress may (randomly) move the caret to the beginning or end
// of the document. If the operation also mutates the selection
// range, the old selection or the new selection may be used, or
// neither.
// As long as the arrow is pressed during the busy operation, it
// doesn't seem to matter that the keydown and keypress events
// aren't generated until afterwards, or that the arrow movement
// can still be stopped (meaning it hasn't been performed yet);
// Firefox must be preserving some old information about the
// selection or the DOM from when the key was initially pressed.
// However, it also doesn't seem to matter when the key was
// actually pressed relative to the time of the mutation within
// the prolonged operation. Also, even in very controlled tests
// (like a mutation followed by a long period of busyWaiting), the
// problem shows up often but not every time, with no discernable
// pattern. Who knows, it could have something to do with the
// caret-blinking timer, or DOM changes not being applied
// immediately.
// This problem, mercifully, does not show up at all in IE or
// Safari. My solution is to have my own, full-featured arrow-key
// implementation for Firefox.
// Note that the problem addressed here is potentially very subtle,
// especially if the operation is quick and is timed to usually happen
// when the user is idle.
// features:
// - 'up' and 'down' arrows preserve column when passing through shorter lines
// - shift-arrows extend the "focus" point, which may be start or end of range
// - the focus point is kept horizontally and vertically scrolled into view
// - arrows without shift cause caret to move to beginning or end of selection (left,right)
// or move focus point up or down a line (up,down)
// - command-(left,right,up,down) on Mac acts like (line-start, line-end, doc-start, doc-end)
// - takes wrapping into account when doesWrap is true, i.e. up-arrow and down-arrow move
// between the virtual lines within a wrapped line; this was difficult, and unfortunately
// requires mutating the DOM to get the necessary information
var savedFocusColumn = 0; // a value of 0 has no effect
var updatingSelectionNow = false;
function getVirtualLineView(lineNum)
{
var lineNode = rep.lines.atIndex(lineNum).lineNode;
while (lineNode.firstChild && isBlockElement(lineNode.firstChild))
{
lineNode = lineNode.firstChild;
}
return makeVirtualLineView(lineNode);
}
function markerlessLineAndChar(line, chr)
{
return [line, chr - rep.lines.atIndex(line).lineMarker];
}
function markerfulLineAndChar(line, chr)
{
return [line, chr + rep.lines.atIndex(line).lineMarker];
}
return {
notifySelectionChanged: function()
{
if (!updatingSelectionNow)
{
savedFocusColumn = 0;
}
},
handleKeyEvent: function(evt)
{
// returns "true" if handled
if (evt.type != "keypress") return false;
var keyCode = evt.keyCode;
if (keyCode < 37 || keyCode > 40) return false;
incorporateUserChanges();
if (!(rep.selStart && rep.selEnd)) return true;
// {byWord,toEnd,normal}
var moveMode = (evt.altKey ? "byWord" : (evt.ctrlKey ? "byWord" : (evt.metaKey ? "toEnd" : "normal")));
var anchorCaret = markerlessLineAndChar(rep.selStart[0], rep.selStart[1]);
var focusCaret = markerlessLineAndChar(rep.selEnd[0], rep.selEnd[1]);
var wasCaret = isCaret();
if (rep.selFocusAtStart)
{
var tmp = anchorCaret;
anchorCaret = focusCaret;
focusCaret = tmp;
}
var K_UP = 38,
K_DOWN = 40,
K_LEFT = 37,
K_RIGHT = 39;
var dontMove = false;
if (wasCaret && !evt.shiftKey)
{
// collapse, will mutate both together
anchorCaret = focusCaret;
}
else if ((!wasCaret) && (!evt.shiftKey))
{
if (keyCode == K_LEFT)
{
// place caret at beginning
if (rep.selFocusAtStart) anchorCaret = focusCaret;
else focusCaret = anchorCaret;
if (moveMode == "normal") dontMove = true;
}
else if (keyCode == K_RIGHT)
{
// place caret at end
if (rep.selFocusAtStart) focusCaret = anchorCaret;
else anchorCaret = focusCaret;
if (moveMode == "normal") dontMove = true;
}
else
{
// collapse, will mutate both together
anchorCaret = focusCaret;
}
}
if (!dontMove)
{
function lineLength(i)
{
var entry = rep.lines.atIndex(i);
return entry.text.length - entry.lineMarker;
}
function lineText(i)
{
var entry = rep.lines.atIndex(i);
return entry.text.substring(entry.lineMarker);
}
if (keyCode == K_UP || keyCode == K_DOWN)
{
var up = (keyCode == K_UP);
var canChangeLines = ((up && focusCaret[0]) || ((!up) && focusCaret[0] < rep.lines.length() - 1));
var virtualLineView, virtualLineSpot, canChangeVirtualLines = false;
if (doesWrap)
{
virtualLineView = getVirtualLineView(focusCaret[0]);
virtualLineSpot = virtualLineView.getVLineAndOffsetForChar(focusCaret[1]);
canChangeVirtualLines = ((up && virtualLineSpot.vline > 0) || ((!up) && virtualLineSpot.vline < (
virtualLineView.getNumVirtualLines() - 1)));
}
var newColByVirtualLineChange;
if (moveMode == "toEnd")
{
if (up)
{
focusCaret[0] = 0;
focusCaret[1] = 0;
}
else
{
focusCaret[0] = rep.lines.length() - 1;
focusCaret[1] = lineLength(focusCaret[0]);
}
}
else if (moveMode == "byWord")
{
// move by "paragraph", a feature that Firefox lacks but IE and Safari both have
if (up)
{
if (focusCaret[1] === 0 && canChangeLines)
{
focusCaret[0]--;
focusCaret[1] = 0;
}
else focusCaret[1] = 0;
}
else
{
var lineLen = lineLength(focusCaret[0]);
if (browser.windows)
{
if (canChangeLines)
{
focusCaret[0]++;
focusCaret[1] = 0;
}
else
{
focusCaret[1] = lineLen;
}
}
else
{
if (focusCaret[1] == lineLen && canChangeLines)
{
focusCaret[0]++;
focusCaret[1] = lineLength(focusCaret[0]);
}
else
{
focusCaret[1] = lineLen;
}
}
}
savedFocusColumn = 0;
}
else if (canChangeVirtualLines)
{
var vline = virtualLineSpot.vline;
var offset = virtualLineSpot.offset;
if (up) vline--;
else vline++;
if (savedFocusColumn > offset) offset = savedFocusColumn;
else
{
savedFocusColumn = offset;
}
var newSpot = virtualLineView.getCharForVLineAndOffset(vline, offset);
focusCaret[1] = newSpot.lineChar;
}
else if (canChangeLines)
{
if (up) focusCaret[0]--;
else focusCaret[0]++;
var offset = focusCaret[1];
if (doesWrap)
{
offset = virtualLineSpot.offset;
}
if (savedFocusColumn > offset) offset = savedFocusColumn;
else
{
savedFocusColumn = offset;
}
if (doesWrap)
{
var newLineView = getVirtualLineView(focusCaret[0]);
var vline = (up ? newLineView.getNumVirtualLines() - 1 : 0);
var newSpot = newLineView.getCharForVLineAndOffset(vline, offset);
focusCaret[1] = newSpot.lineChar;
}
else
{
var lineLen = lineLength(focusCaret[0]);
if (offset > lineLen) offset = lineLen;
focusCaret[1] = offset;
}
}
else
{
if (up) focusCaret[1] = 0;
else focusCaret[1] = lineLength(focusCaret[0]);
savedFocusColumn = 0;
}
}
else if (keyCode == K_LEFT || keyCode == K_RIGHT)
{
var left = (keyCode == K_LEFT);
if (left)
{
if (moveMode == "toEnd") focusCaret[1] = 0;
else if (focusCaret[1] > 0)
{
if (moveMode == "byWord")
{
focusCaret[1] = moveByWordInLine(lineText(focusCaret[0]), focusCaret[1], false);
}
else
{
focusCaret[1]--;
}
}
else if (focusCaret[0] > 0)
{
focusCaret[0]--;
focusCaret[1] = lineLength(focusCaret[0]);
if (moveMode == "byWord")
{
focusCaret[1] = moveByWordInLine(lineText(focusCaret[0]), focusCaret[1], false);
}
}
}
else
{
var lineLen = lineLength(focusCaret[0]);
if (moveMode == "toEnd") focusCaret[1] = lineLen;
else if (focusCaret[1] < lineLen)
{
if (moveMode == "byWord")
{
focusCaret[1] = moveByWordInLine(lineText(focusCaret[0]), focusCaret[1], true);
}
else
{
focusCaret[1]++;
}
}
else if (focusCaret[0] < rep.lines.length() - 1)
{
focusCaret[0]++;
focusCaret[1] = 0;
if (moveMode == "byWord")
{
focusCaret[1] = moveByWordInLine(lineText(focusCaret[0]), focusCaret[1], true);
}
}
}
savedFocusColumn = 0;
}
}
var newSelFocusAtStart = ((focusCaret[0] < anchorCaret[0]) || (focusCaret[0] == anchorCaret[0] && focusCaret[1] < anchorCaret[1]));
var newSelStart = (newSelFocusAtStart ? focusCaret : anchorCaret);
var newSelEnd = (newSelFocusAtStart ? anchorCaret : focusCaret);
updatingSelectionNow = true;
performSelectionChange(markerfulLineAndChar(newSelStart[0], newSelStart[1]), markerfulLineAndChar(newSelEnd[0], newSelEnd[1]), newSelFocusAtStart);
updatingSelectionNow = false;
currentCallStack.userChangedSelection = true;
return true;
}
};
})());
var lineNumbersShown; var lineNumbersShown;
var sideDivInner; var sideDivInner;

View file

@ -107,12 +107,16 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded)
{ {
newpos = Number(newpos); newpos = Number(newpos);
if (newpos < 0 || newpos > sliderLength) return; if (newpos < 0 || newpos > sliderLength) return;
if(!newpos){
newpos = 0; // stops it from displaying NaN if newpos isn't set
}
window.location.hash = "#" + newpos; window.location.hash = "#" + newpos;
$("#ui-slider-handle").css('left', newpos * ($("#ui-slider-bar").width() - 2) / (sliderLength * 1.0)); $("#ui-slider-handle").css('left', newpos * ($("#ui-slider-bar").width() - 2) / (sliderLength * 1.0));
$("a.tlink").map(function() $("a.tlink").map(function()
{ {
$(this).attr('href', $(this).attr('thref').replace("%revision%", newpos)); $(this).attr('href', $(this).attr('thref').replace("%revision%", newpos));
}); });
$("#revision_label").html(html10n.get("timeslider.version", { "version": newpos})); $("#revision_label").html(html10n.get("timeslider.version", { "version": newpos}));
if (newpos == 0) if (newpos == 0)
@ -456,31 +460,6 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded)
if (clientVars) if (clientVars)
{ {
if (clientVars.fullWidth)
{
$("#padpage").css('width', '100%');
$("#revision").css('position', "absolute")
$("#revision").css('right', "20px")
$("#revision").css('top', "20px")
$("#padmain").css('left', '0px');
$("#padmain").css('right', '197px');
$("#padmain").css('width', 'auto');
$("#rightbars").css('right', '7px');
$("#rightbars").css('margin-right', '0px');
$("#timeslider").css('width', 'auto');
}
if (clientVars.disableRightBar)
{
$("#rightbars").css('display', 'none');
$('#padmain').css('width', 'auto');
if (clientVars.fullWidth) $("#padmain").css('right', '7px');
else $("#padmain").css('width', '860px');
$("#revision").css('position', "absolute");
$("#revision").css('right', "20px");
$("#revision").css('top', "20px");
}
$("#timeslider").show(); $("#timeslider").show();
var startPos = clientVars.collab_client_vars.rev; var startPos = clientVars.collab_client_vars.rev;

View file

@ -294,8 +294,8 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options, _pad)
if (newRev != (oldRev + 1)) if (newRev != (oldRev + 1))
{ {
dmesg("bad message revision on NEW_CHANGES: " + newRev + " not " + (oldRev + 1)); top.console.warn("bad message revision on NEW_CHANGES: " + newRev + " not " + (oldRev + 1));
setChannelState("DISCONNECTED", "badmessage_newchanges"); // setChannelState("DISCONNECTED", "badmessage_newchanges");
return; return;
} }
msgQueue.push(msg); msgQueue.push(msg);
@ -304,8 +304,8 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options, _pad)
if (newRev != (rev + 1)) if (newRev != (rev + 1))
{ {
dmesg("bad message revision on NEW_CHANGES: " + newRev + " not " + (rev + 1)); top.console.warn("bad message revision on NEW_CHANGES: " + newRev + " not " + (rev + 1));
setChannelState("DISCONNECTED", "badmessage_newchanges"); // setChannelState("DISCONNECTED", "badmessage_newchanges");
return; return;
} }
rev = newRev; rev = newRev;
@ -318,8 +318,8 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options, _pad)
{ {
if (newRev != (msgQueue[msgQueue.length - 1].newRev + 1)) if (newRev != (msgQueue[msgQueue.length - 1].newRev + 1))
{ {
dmesg("bad message revision on ACCEPT_COMMIT: " + newRev + " not " + (msgQueue[msgQueue.length - 1][0] + 1)); top.console.warn("bad message revision on ACCEPT_COMMIT: " + newRev + " not " + (msgQueue[msgQueue.length - 1][0] + 1));
setChannelState("DISCONNECTED", "badmessage_acceptcommit"); // setChannelState("DISCONNECTED", "badmessage_acceptcommit");
return; return;
} }
msgQueue.push(msg); msgQueue.push(msg);
@ -328,8 +328,8 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options, _pad)
if (newRev != (rev + 1)) if (newRev != (rev + 1))
{ {
dmesg("bad message revision on ACCEPT_COMMIT: " + newRev + " not " + (rev + 1)); top.console.warn("bad message revision on ACCEPT_COMMIT: " + newRev + " not " + (rev + 1));
setChannelState("DISCONNECTED", "badmessage_acceptcommit"); // setChannelState("DISCONNECTED", "badmessage_acceptcommit");
return; return;
} }
rev = newRev; rev = newRev;

View file

@ -30,8 +30,7 @@ var Security = require('./security');
var hooks = require('./pluginfw/hooks'); var hooks = require('./pluginfw/hooks');
var _ = require('./underscore'); var _ = require('./underscore');
var lineAttributeMarker = require('./linestylefilter').lineAttributeMarker; var lineAttributeMarker = require('./linestylefilter').lineAttributeMarker;
var Ace2Common = require('./ace2_common'); var noop = function(){};
var noop = Ace2Common.noop;
var domline = {}; var domline = {};

View file

@ -23,27 +23,27 @@
window.html10n = (function(window, document, undefined) { window.html10n = (function(window, document, undefined) {
// fix console // fix console
var console = window.console var console = window.console;
function interceptConsole(method){ function interceptConsole(method){
if (!console) return function() {} if (!console) return function() {};
var original = console[method] var original = console[method];
// do sneaky stuff // do sneaky stuff
if (original.bind){ if (original.bind){
// Do this for normal browsers // Do this for normal browsers
return original.bind(console) return original.bind(console);
}else{ }else{
return function() { return function() {
// Do this for IE // Do this for IE
var message = Array.prototype.slice.apply(arguments).join(' ') var message = Array.prototype.slice.apply(arguments).join(' ');
original(message) original(message);
} }
} }
} }
var consoleLog = interceptConsole('log') var consoleLog = interceptConsole('log')
, consoleWarn = interceptConsole('warn') , consoleWarn = interceptConsole('warn')
, consoleError = interceptConsole('warn') , consoleError = interceptConsole('warn');
// fix Array.prototype.instanceOf in, guess what, IE! <3 // fix Array.prototype.instanceOf in, guess what, IE! <3
@ -100,7 +100,7 @@ window.html10n = (function(window, document, undefined) {
this._events = this._events || {}; this._events = this._events || {};
if( event in this._events === false ) return; if( event in this._events === false ) return;
for(var i = 0; i < this._events[event].length; i++){ for(var i = 0; i < this._events[event].length; i++){
this._events[event][i].apply(this, Array.prototype.slice.call(arguments, 1)) this._events[event][i].apply(this, Array.prototype.slice.call(arguments, 1));
} }
} }
}; };
@ -122,50 +122,50 @@ window.html10n = (function(window, document, undefined) {
* and caching all necessary resources * and caching all necessary resources
*/ */
function Loader(resources) { function Loader(resources) {
this.resources = resources this.resources = resources;
this.cache = {} // file => contents this.cache = {}; // file => contents
this.langs = {} // lang => strings this.langs = {}; // lang => strings
} }
Loader.prototype.load = function(lang, cb) { Loader.prototype.load = function(lang, cb) {
if(this.langs[lang]) return cb() if(this.langs[lang]) return cb();
if (this.resources.length > 0) { if (this.resources.length > 0) {
var reqs = 0; var reqs = 0;
for (var i=0, n=this.resources.length; i < n; i++) { for (var i=0, n=this.resources.length; i < n; i++) {
this.fetch(this.resources[i], lang, function(e) { this.fetch(this.resources[i], lang, function(e) {
reqs++; reqs++;
if(e) return setTimeout(function(){ throw e }, 0) if(e) return setTimeout(function(){ throw e }, 0);
if (reqs < n) return;// Call back once all reqs are completed if (reqs < n) return;// Call back once all reqs are completed
cb && cb() cb && cb();
}) })
} }
} }
} }
Loader.prototype.fetch = function(href, lang, cb) { Loader.prototype.fetch = function(href, lang, cb) {
var that = this var that = this;
if (this.cache[href]) { if (this.cache[href]) {
this.parse(lang, href, this.cache[href], cb) this.parse(lang, href, this.cache[href], cb)
return; return;
} }
var xhr = new XMLHttpRequest() var xhr = new XMLHttpRequest();
xhr.open('GET', href, /*async: */true) xhr.open('GET', href, /*async: */true);
if (xhr.overrideMimeType) { if (xhr.overrideMimeType) {
xhr.overrideMimeType('application/json; charset=utf-8'); xhr.overrideMimeType('application/json; charset=utf-8');
} }
xhr.onreadystatechange = function() { xhr.onreadystatechange = function() {
if (xhr.readyState == 4) { if (xhr.readyState == 4) {
if (xhr.status == 200 || xhr.status === 0) { if (xhr.status == 200 || xhr.status === 0) {
var data = JSON.parse(xhr.responseText) var data = JSON.parse(xhr.responseText);
that.cache[href] = data that.cache[href] = data;
// Pass on the contents for parsing // Pass on the contents for parsing
that.parse(lang, href, data, cb) that.parse(lang, href, data, cb);
} else { } else {
cb(new Error('Failed to load '+href)) cb(new Error('Failed to load '+href));
} }
} }
}; };
@ -174,39 +174,39 @@ window.html10n = (function(window, document, undefined) {
Loader.prototype.parse = function(lang, currHref, data, cb) { Loader.prototype.parse = function(lang, currHref, data, cb) {
if ('object' != typeof data) { if ('object' != typeof data) {
cb(new Error('A file couldn\'t be parsed as json.')) cb(new Error('A file couldn\'t be parsed as json.'));
return return;
} }
if (!data[lang]) lang = lang.substr(0, lang.indexOf('-') == -1? lang.length : lang.indexOf('-')) if (!data[lang]) lang = lang.substr(0, lang.indexOf('-') == -1? lang.length : lang.indexOf('-'));
if (!data[lang]) { if (!data[lang]) {
cb(new Error('Couldn\'t find translations for '+lang)) cb(new Error('Couldn\'t find translations for '+lang));
return return;
} }
if ('string' == typeof data[lang]) { if ('string' == typeof data[lang]) {
// Import rule // Import rule
// absolute path // absolute path
var importUrl = data[lang] var importUrl = data[lang];
// relative path // relative path
if(data[lang].indexOf("http") != 0 && data[lang].indexOf("/") != 0) { if(data[lang].indexOf("http") != 0 && data[lang].indexOf("/") != 0) {
importUrl = currHref+"/../"+data[lang] importUrl = currHref+"/../"+data[lang];
} }
this.fetch(importUrl, lang, cb) this.fetch(importUrl, lang, cb);
return return;
} }
if ('object' != typeof data[lang]) { if ('object' != typeof data[lang]) {
cb(new Error('Translations should be specified as JSON objects!')) cb(new Error('Translations should be specified as JSON objects!'));
return return;
} }
this.langs[lang] = data[lang] this.langs[lang] = data[lang];
// TODO: Also store accompanying langs // TODO: Also store accompanying langs
cb() cb();
} }
@ -216,11 +216,11 @@ window.html10n = (function(window, document, undefined) {
var html10n = var html10n =
{ language : null { language : null
} }
MicroEvent.mixin(html10n) MicroEvent.mixin(html10n);
html10n.macros = {} html10n.macros = {};
html10n.rtl = ["ar","dv","fa","ha","he","ks","ku","ps","ur","yi"] html10n.rtl = ["ar","dv","fa","ha","he","ks","ku","ps","ur","yi"];
/** /**
* Get rules for plural forms (shared with JetPack), see: * Get rules for plural forms (shared with JetPack), see:
@ -664,14 +664,14 @@ window.html10n = (function(window, document, undefined) {
* @param langs An array of lang codes defining fallbacks * @param langs An array of lang codes defining fallbacks
*/ */
html10n.localize = function(langs) { html10n.localize = function(langs) {
var that = this var that = this;
// if only one string => create an array // if only one string => create an array
if ('string' == typeof langs) langs = [langs] if ('string' == typeof langs) langs = [langs];
this.build(langs, function(er, translations) { this.build(langs, function(er, translations) {
html10n.translations = translations html10n.translations = translations;
html10n.translateElement(translations) html10n.translateElement(translations);
that.trigger('localized') that.trigger('localized');
}) })
} }
@ -682,78 +682,78 @@ window.html10n = (function(window, document, undefined) {
* @param element A DOM element, if omitted, the document element will be used * @param element A DOM element, if omitted, the document element will be used
*/ */
html10n.translateElement = function(translations, element) { html10n.translateElement = function(translations, element) {
element = element || document.documentElement element = element || document.documentElement;
var children = element? getTranslatableChildren(element) : document.childNodes; var children = element? getTranslatableChildren(element) : document.childNodes;
for (var i=0, n=children.length; i < n; i++) { for (var i=0, n=children.length; i < n; i++) {
this.translateNode(translations, children[i]) this.translateNode(translations, children[i]);
} }
// translate element itself if necessary // translate element itself if necessary
this.translateNode(translations, element) this.translateNode(translations, element);
} }
function asyncForEach(list, iterator, cb) { function asyncForEach(list, iterator, cb) {
var i = 0 var i = 0
, n = list.length , n = list.length;
iterator(list[i], i, function each(err) { iterator(list[i], i, function each(err) {
if(err) consoleLog(err) if(err) consoleLog(err);
i++ i++;
if (i < n) return iterator(list[i],i, each); if (i < n) return iterator(list[i],i, each);
cb() cb();
}) })
} }
function getTranslatableChildren(element) { function getTranslatableChildren(element) {
if(!document.querySelectorAll) { if(!document.querySelectorAll) {
if (!element) return [] if (!element) return [];
var nodes = element.getElementsByTagName('*') var nodes = element.getElementsByTagName('*')
, l10nElements = [] , l10nElements = [];
for (var i=0, n=nodes.length; i < n; i++) { for (var i=0, n=nodes.length; i < n; i++) {
if (nodes[i].getAttribute('data-l10n-id')) if (nodes[i].getAttribute('data-l10n-id'))
l10nElements.push(nodes[i]); l10nElements.push(nodes[i]);
} }
return l10nElements return l10nElements;
} }
return element.querySelectorAll('*[data-l10n-id]') return element.querySelectorAll('*[data-l10n-id]');
} }
html10n.get = function(id, args) { html10n.get = function(id, args) {
var translations = html10n.translations var translations = html10n.translations;
if(!translations) return consoleWarn('No translations available (yet)') if(!translations) return consoleWarn('No translations available (yet)');
if(!translations[id]) return consoleWarn('Could not find string '+id) if(!translations[id]) return consoleWarn('Could not find string '+id);
// apply args // apply args
var str = substArguments(translations[id], args) var str = substArguments(translations[id], args);
// apply macros // apply macros
return substMacros(id, str, args) return substMacros(id, str, args);
// replace {{arguments}} with their values or the // replace {{arguments}} with their values or the
// associated translation string (based on its key) // associated translation string (based on its key)
function substArguments(str, args) { function substArguments(str, args) {
var reArgs = /\{\{\s*([a-zA-Z\.]+)\s*\}\}/ var reArgs = /\{\{\s*([a-zA-Z\.]+)\s*\}\}/
, match , match;
while (match = reArgs.exec(str)) { while (match = reArgs.exec(str)) {
if (!match || match.length < 2) if (!match || match.length < 2)
return str // argument key not found return str; // argument key not found
var arg = match[1] var arg = match[1]
, sub = '' , sub = '';
if (arg in args) { if (arg in args) {
sub = args[arg] sub = args[arg];
} else if (arg in translations) { } else if (arg in translations) {
sub = translations[arg] sub = translations[arg];
} else { } else {
consoleWarn('Could not find argument {{' + arg + '}}') consoleWarn('Could not find argument {{' + arg + '}}');
return str return str;
} }
str = str.substring(0, match.index) + sub + str.substr(match.index + match[0].length) str = str.substring(0, match.index) + sub + str.substr(match.index + match[0].length);
} }
return str return str;
} }
// replace {[macros]} with their values // replace {[macros]} with their values
@ -766,21 +766,21 @@ window.html10n = (function(window, document, undefined) {
// a macro has been found // a macro has been found
// Note: at the moment, only one parameter is supported // Note: at the moment, only one parameter is supported
var macroName = reMatch[1] var macroName = reMatch[1]
, paramName = reMatch[2] , paramName = reMatch[2];
if (!(macroName in gMacros)) return str if (!(macroName in gMacros)) return str;
var param var param;
if (args && paramName in args) { if (args && paramName in args) {
param = args[paramName] param = args[paramName];
} else if (paramName in translations) { } else if (paramName in translations) {
param = translations[paramName] param = translations[paramName];
} }
// there's no macro parser yet: it has to be defined in gMacros // there's no macro parser yet: it has to be defined in gMacros
var macro = html10n.macros[macroName] var macro = html10n.macros[macroName];
str = macro(translations, key, str, param) str = macro(translations, key, str, param);
return str return str;
} }
} }
@ -788,26 +788,26 @@ window.html10n = (function(window, document, undefined) {
* Applies translations to a DOM node (recursive) * Applies translations to a DOM node (recursive)
*/ */
html10n.translateNode = function(translations, node) { html10n.translateNode = function(translations, node) {
var str = {} var str = {};
// get id // get id
str.id = node.getAttribute('data-l10n-id') str.id = node.getAttribute('data-l10n-id');
if (!str.id) return if (!str.id) return;
if(!translations[str.id]) return consoleWarn('Couldn\'t find translation key '+str.id) if(!translations[str.id]) return consoleWarn('Couldn\'t find translation key '+str.id);
// get args // get args
if(window.JSON) { if(window.JSON) {
str.args = JSON.parse(node.getAttribute('data-l10n-args')) str.args = JSON.parse(node.getAttribute('data-l10n-args'));
}else{ }else{
try{ try{
str.args = eval(node.getAttribute('data-l10n-args')) str.args = eval(node.getAttribute('data-l10n-args'));
}catch(e) { }catch(e) {
consoleWarn('Couldn\'t parse args for '+str.id) consoleWarn('Couldn\'t parse args for '+str.id);
} }
} }
str.str = html10n.get(str.id, str.args) str.str = html10n.get(str.id, str.args);
// get attribute name to apply str to // get attribute name to apply str to
var prop var prop
@ -817,31 +817,31 @@ window.html10n = (function(window, document, undefined) {
, "innerHTML": 1 , "innerHTML": 1
, "alt": 1 , "alt": 1
, "textContent": 1 , "textContent": 1
} };
if (index > 0 && str.id.substr(index + 1) in attrList) { // an attribute has been specified if (index > 0 && str.id.substr(index + 1) in attrList) { // an attribute has been specified
prop = str.id.substr(index + 1) prop = str.id.substr(index + 1);
} else { // no attribute: assuming text content by default } else { // no attribute: assuming text content by default
prop = document.body.textContent ? 'textContent' : 'innerText' prop = document.body.textContent ? 'textContent' : 'innerText';
} }
// Apply translation // Apply translation
if (node.children.length === 0 || prop != 'textContent') { if (node.children.length === 0 || prop != 'textContent') {
node[prop] = str.str node[prop] = str.str;
} else { } else {
var children = node.childNodes, var children = node.childNodes,
found = false found = false;
for (var i=0, n=children.length; i < n; i++) { for (var i=0, n=children.length; i < n; i++) {
if (children[i].nodeType === 3 && /\S/.test(children[i].textContent)) { if (children[i].nodeType === 3 && /\S/.test(children[i].textContent)) {
if (!found) { if (!found) {
children[i].nodeValue = str.str children[i].nodeValue = str.str;
found = true found = true;
} else { } else {
children[i].nodeValue = '' children[i].nodeValue = '';
} }
} }
} }
if (!found) { if (!found) {
consoleWarn('Unexpected error: could not translate element content for key '+str.id, node) consoleWarn('Unexpected error: could not translate element content for key '+str.id, node);
} }
} }
} }
@ -852,32 +852,32 @@ window.html10n = (function(window, document, undefined) {
*/ */
html10n.build = function(langs, cb) { html10n.build = function(langs, cb) {
var that = this var that = this
, build = {} , build = {};
asyncForEach(langs, function (lang, i, next) { asyncForEach(langs, function (lang, i, next) {
if(!lang) return next(); if(!lang) return next();
that.loader.load(lang, next) that.loader.load(lang, next);
}, function() { }, function() {
var lang var lang;
langs.reverse() langs.reverse();
// loop through priority array... // loop through priority array...
for (var i=0, n=langs.length; i < n; i++) { for (var i=0, n=langs.length; i < n; i++) {
lang = langs[i] lang = langs[i];
if(!lang || !(lang in that.loader.langs)) continue; if(!lang || !(lang in that.loader.langs)) continue;
// ... and apply all strings of the current lang in the list // ... and apply all strings of the current lang in the list
// to our build object // to our build object
for (var string in that.loader.langs[lang]) { for (var string in that.loader.langs[lang]) {
build[string] = that.loader.langs[lang][string] build[string] = that.loader.langs[lang][string];
} }
// the last applied lang will be exposed as the // the last applied lang will be exposed as the
// lang the page was translated to // lang the page was translated to
that.language = lang that.language = lang;
} }
cb(null, build) cb(null, build);
}) })
} }
@ -893,8 +893,8 @@ window.html10n = (function(window, document, undefined) {
* Returns the direction of the language returned be html10n#getLanguage * Returns the direction of the language returned be html10n#getLanguage
*/ */
html10n.getDirection = function() { html10n.getDirection = function() {
var langCode = this.language.indexOf('-') == -1? this.language : this.language.substr(0, this.language.indexOf('-')) var langCode = this.language.indexOf('-') == -1? this.language : this.language.substr(0, this.language.indexOf('-'));
return html10n.rtl.indexOf(langCode) == -1? 'ltr' : 'rtl' return html10n.rtl.indexOf(langCode) == -1? 'ltr' : 'rtl';
} }
/** /**
@ -903,28 +903,28 @@ window.html10n = (function(window, document, undefined) {
html10n.index = function () { html10n.index = function () {
// Find all <link>s // Find all <link>s
var links = document.getElementsByTagName('link') var links = document.getElementsByTagName('link')
, resources = [] , resources = [];
for (var i=0, n=links.length; i < n; i++) { for (var i=0, n=links.length; i < n; i++) {
if (links[i].type != 'application/l10n+json') if (links[i].type != 'application/l10n+json')
continue; continue;
resources.push(links[i].href) resources.push(links[i].href);
} }
this.loader = new Loader(resources) this.loader = new Loader(resources);
this.trigger('indexed') this.trigger('indexed');
} }
if (document.addEventListener) // modern browsers and IE9+ if (document.addEventListener) // modern browsers and IE9+
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
html10n.index() html10n.index();
}, false) }, false);
else if (window.attachEvent) else if (window.attachEvent)
window.attachEvent('onload', function() { window.attachEvent('onload', function() {
html10n.index() html10n.index();
}, false) }, false);
// gettext-like shortcut // gettext-like shortcut
if (window._ === undefined) if (window._ === undefined)
window._ = html10n.get; window._ = html10n.get;
return html10n return html10n;
})(window, document) })(window, document);

9961
src/static/js/jquery.js vendored

File diff suppressed because it is too large Load diff

50
src/static/js/jquery_browser.js vendored Normal file
View file

@ -0,0 +1,50 @@
/*
Copied from jQuery 1.8, the last jquery version with browser recognition support
*/
(function(){
// Use of jQuery.browser is frowned upon.
// More details: http://api.jquery.com/jQuery.browser
// jQuery.uaMatch maintained for back-compat
var uaMatch = function( ua ) {
ua = ua.toLowerCase();
var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) ||
/(webkit)[ \/]([\w.]+)/.exec( ua ) ||
/(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) ||
/(msie) ([\w.]+)/.exec( ua ) ||
ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) ||
[];
return {
browser: match[ 1 ] || "",
version: match[ 2 ] || "0"
};
};
var userAgent = navigator.userAgent;
var matched = uaMatch(userAgent);
var browser = {};
if ( matched.browser ) {
browser[ matched.browser ] = true;
browser.version = matched.version;
}
// Chrome is Webkit, but Webkit is also Safari.
if ( browser.chrome ) {
browser.webkit = true;
} else if ( browser.webkit ) {
browser.safari = true;
}
//custom extensions, the original jquery didn't have these
browser.windows = /windows/i.test(userAgent);
browser.mobile = /mobile/i.test(userAgent) || /android/i.test(userAgent);
if(typeof exports !== 'undefined'){
exports.browser = browser;
} else{
$.browser = browser;
}
})();

View file

@ -1,6 +1,6 @@
(function(document) { (function(document) {
// Set language for l10n // 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]; if(language) language = language[1];
html10n.bind('indexed', function() { html10n.bind('indexed', function() {

View file

@ -35,7 +35,6 @@ var chat = require('./chat').chat;
var getCollabClient = require('./collab_client').getCollabClient; var getCollabClient = require('./collab_client').getCollabClient;
var padconnectionstatus = require('./pad_connectionstatus').padconnectionstatus; var padconnectionstatus = require('./pad_connectionstatus').padconnectionstatus;
var padcookie = require('./pad_cookie').padcookie; var padcookie = require('./pad_cookie').padcookie;
var paddocbar = require('./pad_docbar').paddocbar;
var padeditbar = require('./pad_editbar').padeditbar; var padeditbar = require('./pad_editbar').padeditbar;
var padeditor = require('./pad_editor').padeditor; var padeditor = require('./pad_editor').padeditor;
var padimpexp = require('./pad_impexp').padimpexp; var padimpexp = require('./pad_impexp').padimpexp;
@ -391,10 +390,6 @@ var pad = {
{ {
return clientVars.clientIp; return clientVars.clientIp;
}, },
getIsProPad: function()
{
return clientVars.isProPad;
},
getColorPalette: function() getColorPalette: function()
{ {
return clientVars.colorPalette; return clientVars.colorPalette;
@ -467,11 +462,6 @@ var pad = {
} }
// order of inits is important here: // order of inits is important here:
padcookie.init(clientVars.cookiePrefsToSet, this);
$("#widthprefcheck").click(pad.toggleWidthPref);
// $("#sidebarcheck").click(pad.togglewSidebar);
pad.myUserInfo = { pad.myUserInfo = {
userId: clientVars.userId, userId: clientVars.userId,
name: clientVars.userName, name: clientVars.userName,
@ -488,20 +478,12 @@ var pad = {
$("#specialkeyarea").html("mode: " + String(clientVars.specialKeyTranslation).toUpperCase()); $("#specialkeyarea").html("mode: " + String(clientVars.specialKeyTranslation).toUpperCase());
} }
} }
paddocbar.init(
{
isTitleEditable: pad.getIsProPad(),
initialTitle: clientVars.initialTitle,
initialPassword: clientVars.initialPassword,
guestPolicy: pad.padOptions.guestPolicy
}, this);
padimpexp.init(this); padimpexp.init(this);
padsavedrevs.init(this); padsavedrevs.init(this);
padeditor.init(postAceInit, pad.padOptions.view || {}, this); padeditor.init(postAceInit, pad.padOptions.view || {}, this);
paduserlist.init(pad.myUserInfo, this); paduserlist.init(pad.myUserInfo, this);
// padchat.init(clientVars.chatHistory, pad.myUserInfo);
padconnectionstatus.init(); padconnectionstatus.init();
padmodals.init(this); padmodals.init(this);
@ -553,31 +535,11 @@ var pad = {
{ {
pad.myUserInfo.name = newName; pad.myUserInfo.name = newName;
pad.collabClient.updateUserInfo(pad.myUserInfo); pad.collabClient.updateUserInfo(pad.myUserInfo);
//padchat.handleUserJoinOrUpdate(pad.myUserInfo);
}, },
notifyChangeColor: function(newColorId) notifyChangeColor: function(newColorId)
{ {
pad.myUserInfo.colorId = newColorId; pad.myUserInfo.colorId = newColorId;
pad.collabClient.updateUserInfo(pad.myUserInfo); pad.collabClient.updateUserInfo(pad.myUserInfo);
//padchat.handleUserJoinOrUpdate(pad.myUserInfo);
},
notifyChangeTitle: function(newTitle)
{
pad.collabClient.sendClientMessage(
{
type: 'padtitle',
title: newTitle,
changedBy: pad.myUserInfo.name || "unnamed"
});
},
notifyChangePassword: function(newPass)
{
pad.collabClient.sendClientMessage(
{
type: 'padpassword',
password: newPass,
changedBy: pad.myUserInfo.name || "unnamed"
});
}, },
changePadOption: function(key, value) changePadOption: function(key, value)
{ {
@ -619,7 +581,6 @@ var pad = {
{ {
// order important here // order important here
pad.padOptions.guestPolicy = opts.guestPolicy; pad.padOptions.guestPolicy = opts.guestPolicy;
paddocbar.setGuestPolicy(opts.guestPolicy);
} }
}, },
getPadOptions: function() getPadOptions: function()
@ -629,7 +590,7 @@ var pad = {
}, },
isPadPublic: function() isPadPublic: function()
{ {
return (!pad.getIsProPad()) || (pad.getPadOptions().guestPolicy == 'allow'); return pad.getPadOptions().guestPolicy == 'allow';
}, },
suggestUserName: function(userId, name) suggestUserName: function(userId, name)
{ {
@ -643,17 +604,14 @@ var pad = {
handleUserJoin: function(userInfo) handleUserJoin: function(userInfo)
{ {
paduserlist.userJoinOrUpdate(userInfo); paduserlist.userJoinOrUpdate(userInfo);
//padchat.handleUserJoinOrUpdate(userInfo);
}, },
handleUserUpdate: function(userInfo) handleUserUpdate: function(userInfo)
{ {
paduserlist.userJoinOrUpdate(userInfo); paduserlist.userJoinOrUpdate(userInfo);
//padchat.handleUserJoinOrUpdate(userInfo);
}, },
handleUserLeave: function(userInfo) handleUserLeave: function(userInfo)
{ {
paduserlist.userLeave(userInfo); paduserlist.userLeave(userInfo);
//padchat.handleUserLeave(userInfo);
}, },
handleClientMessage: function(msg) handleClientMessage: function(msg)
{ {
@ -665,18 +623,6 @@ var pad = {
paduserlist.setMyUserInfo(pad.myUserInfo); paduserlist.setMyUserInfo(pad.myUserInfo);
} }
} }
else if (msg.type == 'chat')
{
//padchat.receiveChat(msg);
}
else if (msg.type == 'padtitle')
{
paddocbar.changeTitle(msg.title);
}
else if (msg.type == 'padpassword')
{
paddocbar.changePassword(msg.password);
}
else if (msg.type == 'newRevisionList') else if (msg.type == 'newRevisionList')
{ {
padsavedrevs.newRevisionList(msg.revisionList); padsavedrevs.newRevisionList(msg.revisionList);
@ -769,7 +715,6 @@ var pad = {
} }
padeditor.disable(); padeditor.disable();
padeditbar.disable(); padeditbar.disable();
paddocbar.disable();
padimpexp.disable(); padimpexp.disable();
padconnectionstatus.disconnected(message); padconnectionstatus.disconnected(message);
@ -796,28 +741,10 @@ var pad = {
}, 1000); }, 1000);
} }
// pad.determineSidebarVisibility(isConnected && !isInitialConnect);
pad.determineChatVisibility(isConnected && !isInitialConnect); pad.determineChatVisibility(isConnected && !isInitialConnect);
pad.determineAuthorshipColorsVisibility(); pad.determineAuthorshipColorsVisibility();
}, },
/* determineSidebarVisibility: function(asNowConnectedFeedback)
{
if (pad.isFullyConnected())
{
var setSidebarVisibility = padutils.getCancellableAction("set-sidebar-visibility", function()
{
// $("body").toggleClass('hidesidebar', !! padcookie.getPref('hideSidebar'));
});
window.setTimeout(setSidebarVisibility, asNowConnectedFeedback ? 3000 : 0);
}
else
{
padutils.cancelActions("set-sidebar-visibility");
$("body").removeClass('hidesidebar');
}
},
*/
determineChatVisibility: function(asNowConnectedFeedback){ determineChatVisibility: function(asNowConnectedFeedback){
var chatVisCookie = padcookie.getPref('chatAlwaysVisible'); var chatVisCookie = padcookie.getPref('chatAlwaysVisible');
if(chatVisCookie){ // if the cookie is set for chat always visible if(chatVisCookie){ // if the cookie is set for chat always visible
@ -879,37 +806,6 @@ var pad = {
$('form#reconnectform input.missedChanges').val(JSON.stringify(pad.collabClient.getMissedChanges())); $('form#reconnectform input.missedChanges').val(JSON.stringify(pad.collabClient.getMissedChanges()));
$('form#reconnectform').submit(); $('form#reconnectform').submit();
}, },
toggleWidthPref: function()
{
var newValue = !padcookie.getPref('fullWidth');
padcookie.setPref('fullWidth', newValue);
$("#widthprefcheck").toggleClass('widthprefchecked', !! newValue).toggleClass('widthprefunchecked', !newValue);
pad.handleWidthChange();
},
/*
toggleSidebar: function()
{
var newValue = !padcookie.getPref('hideSidebar');
padcookie.setPref('hideSidebar', newValue);
$("#sidebarcheck").toggleClass('sidebarchecked', !newValue).toggleClass('sidebarunchecked', !! newValue);
pad.determineSidebarVisibility();
},
*/
handleWidthChange: function()
{
var isFullWidth = padcookie.getPref('fullWidth');
if (isFullWidth)
{
$("body").addClass('fullwidth').removeClass('limwidth').removeClass('squish1width').removeClass('squish2width');
}
else
{
$("body").addClass('limwidth').removeClass('fullwidth');
var pageWidth = $(window).width();
$("body").toggleClass('squish1width', (pageWidth < 912 && pageWidth > 812)).toggleClass('squish2width', (pageWidth <= 812));
}
},
// this is called from code put into a frame from the server: // this is called from code put into a frame from the server:
handleImportExportFrameCall: function(callName, varargs) handleImportExportFrameCall: function(callName, varargs)
{ {

View file

@ -43,9 +43,8 @@ var padconnectionstatus = (function()
status = { status = {
what: 'connected' what: 'connected'
}; };
padmodals.showModal('connected'); padmodals.showModal('connected');
padmodals.hideOverlay(500); padmodals.hideOverlay();
}, },
reconnecting: function() reconnecting: function()
{ {
@ -54,7 +53,7 @@ var padconnectionstatus = (function()
}; };
padmodals.showModal('reconnecting'); padmodals.showModal('reconnecting');
padmodals.showOverlay(500); padmodals.showOverlay();
}, },
disconnected: function(msg) disconnected: function(msg)
{ {
@ -73,10 +72,11 @@ var padconnectionstatus = (function()
} }
padmodals.showModal(k); padmodals.showModal(k);
padmodals.showOverlay(500); padmodals.showOverlay();
}, },
isFullyConnected: function() isFullyConnected: function()
{ {
padmodals.hideOverlay();
return status.what == 'connected'; return status.what == 'connected';
}, },
getStatus: function() getStatus: function()

View file

@ -73,7 +73,7 @@ var padcookie = (function()
} }
setRawCookie(stringifyCookie(cookieData)); setRawCookie(stringifyCookie(cookieData));
if (pad.getIsProPad() && (!getRawCookie()) && (!alreadyWarnedAboutNoCookies)) if ((!getRawCookie()) && (!alreadyWarnedAboutNoCookies))
{ {
alert("Warning: it appears that your browser does not have cookies enabled." + " EtherPad uses cookies to keep track of unique users for the purpose" + " of putting a quota on the number of active users. Using EtherPad without " + " cookies may fill up your server's user quota faster than expected."); alert("Warning: it appears that your browser does not have cookies enabled." + " EtherPad uses cookies to keep track of unique users for the purpose" + " of putting a quota on the number of active users. Using EtherPad without " + " cookies may fill up your server's user quota faster than expected.");
alreadyWarnedAboutNoCookies = true; alreadyWarnedAboutNoCookies = true;

View file

@ -1,466 +0,0 @@
/**
* This code is mostly from the old Etherpad. Please help us to comment this code.
* This helps other people to understand this code better and helps them to improve it.
* TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
*/
/**
* Copyright 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var padutils = require('./pad_utils').padutils;
var paddocbar = (function()
{
var isTitleEditable = false;
var isEditingTitle = false;
var isEditingPassword = false;
var enabled = false;
function getPanelOpenCloseAnimator(panelName, panelHeight)
{
var wrapper = $("#" + panelName + "-wrapper");
var openingClass = "docbar" + panelName + "-opening";
var openClass = "docbar" + panelName + "-open";
var closingClass = "docbar" + panelName + "-closing";
function setPanelState(action)
{
$("#docbar").removeClass(openingClass).removeClass(openClass).
removeClass(closingClass);
if (action != "closed")
{
$("#docbar").addClass("docbar" + panelName + "-" + action);
}
}
function openCloseAnimate(state)
{
function pow(x)
{
x = 1 - x;
x *= x * x;
return 1 - x;
}
if (state == -1)
{
// startng to open
setPanelState("opening");
wrapper.css('height', '0');
}
else if (state < 0)
{
// opening
var height = Math.round(pow(state + 1) * (panelHeight - 1)) + 'px';
wrapper.css('height', height);
}
else if (state == 0)
{
// open
setPanelState("open");
wrapper.css('height', panelHeight - 1);
}
else if (state < 1)
{
// closing
setPanelState("closing");
var height = Math.round((1 - pow(state)) * (panelHeight - 1)) + 'px';
wrapper.css('height', height);
}
else if (state == 1)
{
// closed
setPanelState("closed");
wrapper.css('height', '0');
}
}
return padutils.makeShowHideAnimator(openCloseAnimate, false, 25, 500);
}
var currentPanel = null;
function setCurrentPanel(newCurrentPanel)
{
if (currentPanel != newCurrentPanel)
{
currentPanel = newCurrentPanel;
padutils.cancelActions("hide-docbar-panel");
}
}
var panels;
function changePassword(newPass)
{
if ((newPass || null) != (self.password || null))
{
self.password = (newPass || null);
pad.notifyChangePassword(newPass);
}
self.renderPassword();
}
var pad = undefined;
var self = {
title: null,
password: null,
init: function(opts, _pad)
{
pad = _pad;
panels = {
impexp: {
animator: getPanelOpenCloseAnimator("impexp", 160)
},
savedrevs: {
animator: getPanelOpenCloseAnimator("savedrevs", 79)
},
options: {
animator: getPanelOpenCloseAnimator("options", 114)
},
security: {
animator: getPanelOpenCloseAnimator("security", 130)
}
};
isTitleEditable = opts.isTitleEditable;
self.title = opts.initialTitle;
self.password = opts.initialPassword;
$("#docbarimpexp").click(function()
{
self.togglePanel("impexp");
});
$("#docbarsavedrevs").click(function()
{
self.togglePanel("savedrevs");
});
$("#docbaroptions").click(function()
{
self.togglePanel("options");
});
$("#docbarsecurity").click(function()
{
self.togglePanel("security");
});
$("#docbarrenamelink").click(self.editTitle);
$("#padtitlesave").click(function()
{
self.closeTitleEdit(true);
});
$("#padtitlecancel").click(function()
{
self.closeTitleEdit(false);
});
padutils.bindEnterAndEscape($("#padtitleedit"), function()
{
$("#padtitlesave").trigger('click');
}, function()
{
$("#padtitlecancel").trigger('click');
});
$("#options-close").click(function()
{
self.setShownPanel(null);
});
$("#security-close").click(function()
{
self.setShownPanel(null);
});
if (pad.getIsProPad())
{
self.initPassword();
}
enabled = true;
self.render();
// public/private
$("#security-access input").bind("change click", function(evt)
{
pad.changePadOption('guestPolicy', $("#security-access input[name='padaccess']:checked").val());
});
self.setGuestPolicy(opts.guestPolicy);
},
setGuestPolicy: function(newPolicy)
{
$("#security-access input[value='" + newPolicy + "']").attr("checked", "checked");
self.render();
},
initPassword: function()
{
self.renderPassword();
$("#password-clearlink").click(function()
{
changePassword(null);
});
$("#password-setlink, #password-display").click(function()
{
self.enterPassword();
});
$("#password-cancellink").click(function()
{
self.exitPassword(false);
});
$("#password-savelink").click(function()
{
self.exitPassword(true);
});
padutils.bindEnterAndEscape($("#security-passwordedit"), function()
{
self.exitPassword(true);
}, function()
{
self.exitPassword(false);
});
},
enterPassword: function()
{
isEditingPassword = true;
$("#security-passwordedit").val(self.password || '');
self.renderPassword();
$("#security-passwordedit").focus().select();
},
exitPassword: function(accept)
{
isEditingPassword = false;
if (accept)
{
changePassword($("#security-passwordedit").val());
}
else
{
self.renderPassword();
}
},
renderPassword: function()
{
if (isEditingPassword)
{
$("#password-nonedit").hide();
$("#password-inedit").show();
}
else
{
$("#password-nonedit").toggleClass('nopassword', !self.password);
$("#password-setlink").html(self.password ? "Change..." : "Set...");
if (self.password)
{
$("#password-display").html(self.password.replace(/./g, '&#8226;'));
}
else
{
$("#password-display").html("None");
}
$("#password-inedit").hide();
$("#password-nonedit").show();
}
},
togglePanel: function(panelName)
{
if (panelName in panels)
{
if (currentPanel == panelName)
{
self.setShownPanel(null);
}
else
{
self.setShownPanel(panelName);
}
}
},
setShownPanel: function(panelName)
{
function animateHidePanel(panelName, next)
{
var delay = 0;
if (panelName == 'options' && isEditingPassword)
{
// give user feedback that the password they've
// typed in won't actually take effect
self.exitPassword(false);
delay = 500;
}
window.setTimeout(function()
{
panels[panelName].animator.hide();
if (next)
{
next();
}
}, delay);
}
if (!panelName)
{
if (currentPanel)
{
animateHidePanel(currentPanel);
setCurrentPanel(null);
}
}
else if (panelName in panels)
{
if (currentPanel != panelName)
{
if (currentPanel)
{
animateHidePanel(currentPanel, function()
{
panels[panelName].animator.show();
setCurrentPanel(panelName);
});
}
else
{
panels[panelName].animator.show();
setCurrentPanel(panelName);
}
}
}
},
isPanelShown: function(panelName)
{
if (!panelName)
{
return !currentPanel;
}
else
{
return (panelName == currentPanel);
}
},
changeTitle: function(newTitle)
{
self.title = newTitle;
self.render();
},
editTitle: function()
{
if (!enabled)
{
return;
}
$("#padtitleedit").val(self.title);
isEditingTitle = true;
self.render();
$("#padtitleedit").focus().select();
},
closeTitleEdit: function(accept)
{
if (!enabled)
{
return;
}
if (accept)
{
var newTitle = $("#padtitleedit").val();
if (newTitle)
{
newTitle = newTitle.substring(0, 80);
self.title = newTitle;
pad.notifyChangeTitle(newTitle);
}
}
isEditingTitle = false;
self.render();
},
changePassword: function(newPass)
{
if (newPass)
{
self.password = newPass;
}
else
{
self.password = null;
}
self.renderPassword();
},
render: function()
{
if (isEditingTitle)
{
$("#docbarpadtitle").hide();
$("#docbarrenamelink").hide();
$("#padtitleedit").show();
$("#padtitlebuttons").show();
if (!enabled)
{
$("#padtitleedit").attr('disabled', 'disabled');
}
else
{
$("#padtitleedit").removeAttr('disabled');
}
}
else
{
$("#padtitleedit").hide();
$("#padtitlebuttons").hide();
var titleSpan = $("#docbarpadtitle span");
titleSpan.html(padutils.escapeHtml(self.title));
$("#docbarpadtitle").attr('title', (pad.isPadPublic() ? "Public Pad: " : "") + self.title);
$("#docbarpadtitle").show();
if (isTitleEditable)
{
var titleRight = $("#docbarpadtitle").position().left + $("#docbarpadtitle span").position().left + Math.min($("#docbarpadtitle").width(), $("#docbarpadtitle span").width());
$("#docbarrenamelink").css('left', titleRight + 10).show();
}
if (pad.isPadPublic())
{
$("#docbar").addClass("docbar-public");
}
else
{
$("#docbar").removeClass("docbar-public");
}
}
},
disable: function()
{
enabled = false;
self.render();
},
handleResizePage: function()
{
// Side-step circular reference. This should be injected.
var padsavedrevs = require('./pad_savedrevs');
padsavedrevs.handleResizePage();
},
hideLaterIfNoOtherInteraction: function()
{
return padutils.getCancellableAction('hide-docbar-panel', function()
{
self.setShownPanel(null);
});
}
};
return self;
}());
exports.paddocbar = paddocbar;

View file

@ -156,10 +156,7 @@ var padeditbar = (function()
else if (cmd == 'insertorderedlist') ace.ace_doInsertOrderedList(); else if (cmd == 'insertorderedlist') ace.ace_doInsertOrderedList();
else if (cmd == 'indent') else if (cmd == 'indent')
{ {
if (!ace.ace_doIndentOutdent(false)) ace.ace_doIndentOutdent(false);
{
ace.ace_doInsertUnorderedList();
}
} }
else if (cmd == 'outdent') else if (cmd == 'outdent')
{ {

View file

@ -20,14 +20,11 @@
* limitations under the License. * limitations under the License.
*/ */
var paddocbar = require('./pad_docbar').paddocbar;
var padimpexp = (function() var padimpexp = (function()
{ {
///// import ///// import
var currentImportTimer = null; var currentImportTimer = null;
var hidePanelCall = null;
function addImportFrames() function addImportFrames()
{ {
@ -72,7 +69,6 @@ var padimpexp = (function()
var ret = window.confirm(html10n.get("pad.impexp.confirmimport")); var ret = window.confirm(html10n.get("pad.impexp.confirmimport"));
if (ret) if (ret)
{ {
hidePanelCall = paddocbar.hideLaterIfNoOtherInteraction();
currentImportTimer = window.setTimeout(function() currentImportTimer = window.setTimeout(function()
{ {
if (!currentImportTimer) if (!currentImportTimer)
@ -255,11 +251,6 @@ var padimpexp = (function()
$("#exportopena").attr("href", pad_root_path + "/export/odt"); $("#exportopena").attr("href", pad_root_path + "/export/odt");
} }
$("#impexp-close").click(function()
{
paddocbar.setShownPanel(null);
});
addImportFrames(); addImportFrames();
$("#importfileinput").change(fileInputUpdated); $("#importfileinput").change(fileInputUpdated);
$('#importform').submit(fileInputSubmit); $('#importform').submit(fileInputSubmit);

View file

@ -107,14 +107,9 @@ var paduserlist = (function()
function getUserRowHtml(height, data) function getUserRowHtml(height, data)
{ {
var nameHtml; var nameHtml;
var isGuest = (data.id.charAt(0) != 'p');
if (data.name) if (data.name)
{ {
nameHtml = padutils.escapeHtml(data.name); nameHtml = padutils.escapeHtml(data.name);
if (isGuest && pad.getIsProPad())
{
nameHtml += ' ('+_(pad.userlist.guest)+')';
}
} }
else else
{ {

View file

@ -520,11 +520,11 @@ function setupGlobalExceptionHandler() {
$("#editorloadingbox").css("padding", "10px"); $("#editorloadingbox").css("padding", "10px");
$("#editorloadingbox").css("padding-top", "45px"); $("#editorloadingbox").css("padding-top", "45px");
$("#editorloadingbox").html("<div style='text-align:left;color:red;font-size:16px;'><b>An error occured</b><br>The error was reported with the following id: '" + errorId + "'<br><br><span style='color:black;font-weight:bold;font-size:16px'>Please send this error message to us: </span><div style='color:black;font-size:14px'>'" $("#editorloadingbox").html("<div style='text-align:left;color:red;font-size:16px;'><b>An error occured</b><br>The error was reported with the following id: '" + errorId + "'<br><br><span style='color:black;font-weight:bold;font-size:16px'>Please send this error message to us: </span><div style='color:black;font-size:14px'>'"
+ "ErrorId: " + errorId + "<br>UserAgent: " + navigator.userAgent + "<br>" + msg + " in " + url + " at line " + linenumber + "'</div></div>"); + "ErrorId: " + errorId + "<br>URL: " + window.location.href + "<br>UserAgent: " + navigator.userAgent + "<br>" + msg + " in " + url + " at line " + linenumber + "'</div></div>");
} }
//send javascript errors to the server //send javascript errors to the server
var errObj = {errorInfo: JSON.stringify({errorId: errorId, msg: msg, url: url, linenumber: linenumber, userAgent: navigator.userAgent})}; var errObj = {errorInfo: JSON.stringify({errorId: errorId, msg: msg, url: window.location.href, linenumber: linenumber, userAgent: navigator.userAgent})};
var loc = document.location; var loc = document.location;
var url = loc.protocol + "//" + loc.hostname + ":" + loc.port + "/" + loc.pathname.substr(1, loc.pathname.indexOf("/p/")) + "jserror"; var url = loc.protocol + "//" + loc.hostname + ":" + loc.port + "/" + loc.pathname.substr(1, loc.pathname.indexOf("/p/")) + "jserror";

View file

@ -70,11 +70,13 @@ exports.flatten = function (lst) {
exports.callAll = function (hook_name, args) { exports.callAll = function (hook_name, args) {
if (!args) args = {}; if (!args) args = {};
if (exports.plugins){
if (exports.plugins.hooks[hook_name] === undefined) return []; if (exports.plugins.hooks[hook_name] === undefined) return [];
return _.flatten(_.map(exports.plugins.hooks[hook_name], function (hook) { return _.flatten(_.map(exports.plugins.hooks[hook_name], function (hook) {
return hookCallWrapper(hook, hook_name, args); return hookCallWrapper(hook, hook_name, args);
}), true); }), true);
} }
}
exports.aCallAll = function (hook_name, args, cb) { exports.aCallAll = function (hook_name, args, cb) {
if (!args) args = {}; if (!args) args = {};

View file

@ -94,11 +94,12 @@ exports.search = function(query, cache, cb) {
if (er) return cb(er); if (er) return cb(er);
var res = {}; var res = {};
var i = 0; var i = 0;
var pattern = query.pattern.toLowerCase();
for (key in data) { // for every plugin in the data from npm for (key in data) { // for every plugin in the data from npm
if ( key.indexOf(plugins.prefix) == 0 if ( key.indexOf(plugins.prefix) == 0
&& key.indexOf(query.pattern) != -1 && key.indexOf(pattern) != -1
|| key.indexOf(plugins.prefix) == 0 || key.indexOf(plugins.prefix) == 0
&& data[key].description.indexOf(query.pattern) != -1 && data[key].description.indexOf(pattern) != -1
) { // If the name contains ep_ and the search string is in the name or description ) { // If the name contains ep_ and the search string is in the name or description
i++; i++;
if (i > query.offset if (i > query.offset

View file

@ -2,4 +2,9 @@
// Proviedes a require'able version of jQuery without leaking $ and jQuery; // Proviedes a require'able version of jQuery without leaking $ and jQuery;
require('./jquery'); require('./jquery');
exports.jQuery = exports.$ = $.noConflict(true); var jq = window.$.noConflict(true);
//added the old browser recognition
jq.browser = require('./jquery_browser').browser;
exports.jQuery = exports.$ = jq;

View file

@ -29,8 +29,9 @@ var createCookie = require('./pad_utils').createCookie;
var readCookie = require('./pad_utils').readCookie; var readCookie = require('./pad_utils').readCookie;
var randomString = require('./pad_utils').randomString; var randomString = require('./pad_utils').randomString;
var _ = require('./underscore'); var _ = require('./underscore');
var hooks = require('./pluginfw/hooks');
var socket, token, padId, export_links; var token, padId, export_links;
function init() { function init() {
$(document).ready(function () $(document).ready(function ()
@ -106,6 +107,9 @@ function init() {
window.location.reload(); window.location.reload();
}); });
exports.socket = socket; // make the socket available
hooks.aCallAll("postTimesliderInit");
}); });
} }

View file

@ -1,7 +1,8 @@
<!doctype html>
<html> <html>
<head> <head>
<title>Admin Dashboard - Etherpad lite</title> <title>Admin Dashboard - Etherpad</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"> <meta name="viewport" content="width=device-width">
<link rel="stylesheet" href="../static/css/admin.css"> <link rel="stylesheet" href="../static/css/admin.css">
<script src="../static/js/jquery.js"></script> <script src="../static/js/jquery.js"></script>
<script src="../socket.io/socket.io.js"></script> <script src="../socket.io/socket.io.js"></script>
@ -9,13 +10,15 @@
<body> <body>
<div id="wrapper"> <div id="wrapper">
<div class="menu"> <div class="menu">
<h1>Etherpad lite</h1> <h1>Etherpad</h1>
<li><a href="admin/plugins">Plugin manager</a> </li> <ul>
<li><a href="admin/settings">Settings</a> </li> <% e.begin_block("adminMenu"); %>
<li><a href="admin/plugins/info">Troubleshooting information</a> </li> <li><a href="plugins">Plugin manager</a> </li>
<li><a href="settings">Settings</a> </li>
<li><a href="plugins/info">Troubleshooting information</a> </li>
<% e.end_block(); %>
</ul>
</div> </div>
</div> </div>
<div id="topborder"></div>
</body> </body>
</html> </html>

View file

@ -1,20 +1,24 @@
<% <%
var plugins = require("ep_etherpad-lite/static/js/pluginfw/plugins"); var plugins = require("ep_etherpad-lite/static/js/pluginfw/plugins");
%> %>
<!doctype html>
<html> <html>
<head> <head>
<title>Plugin information - Etherpad lite</title> <title>Plugin information - Etherpad</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"> <meta name="viewport" content="width=device-width">
<link rel="stylesheet" href="../../static/css/admin.css"> <link rel="stylesheet" href="../../static/css/admin.css">
</head> </head>
<body> <body>
<div id="wrapper"> <div id="wrapper">
<div class="menu"> <div class="menu">
<h1>Etherpad lite</h1> <h1>Etherpad</h1>
<ul>
<% e.begin_block("adminMenu"); %>
<li><a href="../plugins">Plugin manager</a> </li> <li><a href="../plugins">Plugin manager</a> </li>
<li><a href="../settings">Settings</a> </li> <li><a href="../settings">Settings</a> </li>
<li><a href="../plugins/info">Troubleshooting information</a> </li> <li><a href="../plugins/info">Troubleshooting information</a> </li>
<% e.end_block(); %>
</ul>
</div> </div>
<div class="innerwrapper"> <div class="innerwrapper">
@ -34,6 +38,5 @@
</div> </div>
</div> </div>
<div id="topborder"></div>
</body> </body>
</html> </html>

View file

@ -1,7 +1,8 @@
<!doctype html>
<html> <html>
<head> <head>
<title>Plugin manager - Etherpad lite</title> <title>Plugin manager - Etherpad</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"> <meta name="viewport" content="width=device-width">
<link rel="stylesheet" href="../static/css/admin.css"> <link rel="stylesheet" href="../static/css/admin.css">
<script src="../static/js/jquery.js"></script> <script src="../static/js/jquery.js"></script>
<script src="../socket.io/socket.io.js"></script> <script src="../socket.io/socket.io.js"></script>
@ -19,12 +20,15 @@
<% } %> <% } %>
<div class="menu"> <div class="menu">
<h1>Etherpad lite</h1> <h1>Etherpad</h1>
<ul>
<% e.begin_block("adminMenu"); %>
<li><a href="plugins">Plugin manager</a> </li> <li><a href="plugins">Plugin manager</a> </li>
<li><a href="settings">Settings</a> </li> <li><a href="settings">Settings</a> </li>
<li><a href="plugins/info">Troubleshooting information</a> </li> <li><a href="plugins/info">Troubleshooting information</a> </li>
<% e.end_block(); %>
<div id="progress"><img src="../static/img/loading.gif" alt=""/>&nbsp;&nbsp;<span class="message"></span></div> </ul>
<div id="progress"><img src="../static/img/loading.gif">&nbsp;&nbsp;<span class="message"></span></div>
</div> </div>
<div class="innerwrapper"> <div class="innerwrapper">
@ -40,9 +44,9 @@
</thead> </thead>
<tbody class="template"> <tbody class="template">
<tr id="installed-plugin-template"> <tr id="installed-plugin-template">
<td class="name"></td> <td class="name" data-label="Name"></td>
<td class="description"></td> <td class="description" data-label="Description"></td>
<td class="version"></td> <td class="version" data-label="Version"></td>
<td class="actions"> <td class="actions">
<input type="button" value="Uninstall" class="do-uninstall"> <input type="button" value="Uninstall" class="do-uninstall">
</td> </td>
@ -71,9 +75,9 @@
</thead> </thead>
<tbody class="template"> <tbody class="template">
<tr> <tr>
<td class="name"></td> <td class="name" data-label="Name"></td>
<td class="description"></td> <td class="description" data-label="Description"></td>
<td class="version"></td> <td class="version" data-label="Version"></td>
<td class="actions"> <td class="actions">
<input type="button" value="Install" class="do-install"> <input type="button" value="Install" class="do-install">
</td> </td>
@ -89,6 +93,5 @@
</div> </div>
</div> </div>
<div id="topborder"></div>
</body> </body>
</html> </html>

View file

@ -1,7 +1,8 @@
<!doctype html>
<html> <html>
<head> <head>
<title>Settings - Etherpad lite</title> <title>Settings - Etherpad</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"> <meta name="viewport" content="width=device-width">
<link rel="stylesheet" href="../static/css/admin.css"> <link rel="stylesheet" href="../static/css/admin.css">
<script src="../static/js/jquery.js"></script> <script src="../static/js/jquery.js"></script>
<script src="../socket.io/socket.io.js"></script> <script src="../socket.io/socket.io.js"></script>
@ -23,22 +24,26 @@
<div class="menu"> <div class="menu">
<h1>Etherpad lite</h1> <h1>Etherpad</h1>
<ul>
<% e.begin_block("adminMenu"); %>
<li><a href="plugins">Plugin manager</a> </li> <li><a href="plugins">Plugin manager</a> </li>
<li><a href="settings">Settings</a> </li> <li><a href="settings">Settings</a> </li>
<li><a href="plugins/info">Troubleshooting information</a> </li> <li><a href="plugins/info">Troubleshooting information</a> </li>
<% e.end_block(); %>
</ul>
</div> </div>
<div class="innerwrapper"> <div class="innerwrapper">
<a href='https://github.com/ether/etherpad-lite/wiki/Example-Production-Settings.JSON'>Example production settings template</a> <h2>Current configuration</h2>
<a href='https://github.com/ether/etherpad-lite/wiki/Example-Development-Settings.JSON'>Example development settings template</a>
<textarea class="settings"></textarea> <textarea class="settings"></textarea>
<input type="button" class="settingsButton" id="saveSettings" value="Save Settings"> <input type="button" class="settingsButton" id="saveSettings" value="Save Settings">
<input type="button" class="settingsButton" id="restartEtherpad" value="Restart Etherpad"> <input type="button" class="settingsButton" id="restartEtherpad" value="Restart Etherpad">
<div id="response"></div> <div id="response"></div>
<div class="separator"></div>
<a href='https://github.com/ether/etherpad-lite/wiki/Example-Production-Settings.JSON'>Example production settings template</a>
<a href='https://github.com/ether/etherpad-lite/wiki/Example-Development-Settings.JSON'>Example development settings template</a>
</div> </div>
</div> </div>
<div id="topborder"></div>
</body> </body>
</html> </html>

View file

@ -64,7 +64,8 @@
box-shadow: 0px 1px 8px rgba(0,0,0,0.3); box-shadow: 0px 1px 8px rgba(0,0,0,0.3);
} }
#inner { #inner {
width: 300px; position:relative;
max-width: 300px;
margin: 0 auto; margin: 0 auto;
} }
#button { #button {
@ -100,6 +101,10 @@
text-shadow: 0 1px 1px #fff; text-shadow: 0 1px 1px #fff;
margin: 16px auto 0; margin: 16px auto 0;
} }
#padname{
height:38px;
max-width:280px;
}
form { form {
height: 38px; height: 38px;
background: #fff; background: #fff;
@ -115,7 +120,8 @@
border-radius: 3px; border-radius: 3px;
box-sizing: border-box; box-sizing: border-box;
-moz-box-sizing: border-box; -moz-box-sizing: border-box;
padding: 0 45px 0 10px; line-height:36px; /* IE8 hack */
padding: 0px 45px 0 10px;
*padding: 0; /* IE7 hack */ *padding: 0; /* IE7 hack */
width: 100%; width: 100%;
height: 100%; height: 100%;
@ -125,7 +131,7 @@
} }
button[type="submit"] { button[type="submit"] {
position: absolute; position: absolute;
right: 0; left:253px;
width: 45px; width: 45px;
height: 38px; height: 38px;
} }

View file

@ -40,8 +40,10 @@
<% e.end_block(); %> <% e.end_block(); %>
<link rel="localizations" type="application/l10n+json" href="../../locales.json" /> <link rel="localizations" type="application/l10n+json" href="../../locales.json" />
<% e.begin_block("timesliderScripts"); %>
<script type="text/javascript" src="../../static/js/html10n.js"></script> <script type="text/javascript" src="../../static/js/html10n.js"></script>
<script type="text/javascript" src="../../static/js/l10n.js"></script> <script type="text/javascript" src="../../static/js/l10n.js"></script>
<% e.end_block(); %>
</head> </head>
<% e.begin_block("timesliderBody"); %> <% e.begin_block("timesliderBody"); %>
@ -211,6 +213,8 @@
} }
var plugins = require('ep_etherpad-lite/static/js/pluginfw/client_plugins'); var plugins = require('ep_etherpad-lite/static/js/pluginfw/client_plugins');
var socket = require('ep_etherpad-lite/static/js/timeslider').socket;
plugins.baseURL = baseURL; plugins.baseURL = baseURL;
plugins.update(function () { plugins.update(function () {

View file

@ -85,8 +85,8 @@ var helper = {};
return !$iframe.contents().find("#editorloadingbox").is(":visible"); return !$iframe.contents().find("#editorloadingbox").is(":visible");
}, 50000).done(function(){ }, 50000).done(function(){
helper.padChrome$ = getFrameJQuery( $('#iframe-container iframe')); helper.padChrome$ = getFrameJQuery( $('#iframe-container iframe'));
helper.padOuter$ = getFrameJQuery(helper.padChrome$('iframe.[name="ace_outer"]')); helper.padOuter$ = getFrameJQuery(helper.padChrome$('iframe[name="ace_outer"]'));
helper.padInner$ = getFrameJQuery( helper.padOuter$('iframe.[name="ace_inner"]')); helper.padInner$ = getFrameJQuery( helper.padOuter$('iframe[name="ace_inner"]'));
//disable all animations, this makes tests faster and easier //disable all animations, this makes tests faster and easier
helper.padChrome$.fx.off = true; helper.padChrome$.fx.off = true;

View file

@ -10,6 +10,7 @@
<div id="iframe-container"></div> <div id="iframe-container"></div>
<script src="/static/js/jquery.js"></script> <script src="/static/js/jquery.js"></script>
<script src="/static/js/jquery_browser.js"></script>
<script src="lib/underscore.js"></script> <script src="lib/underscore.js"></script>
<script src="lib/mocha.js"></script> <script src="lib/mocha.js"></script>

View file

@ -179,7 +179,11 @@ $(function(){
//inject spec scripts into the dom //inject spec scripts into the dom
var $body = $('body'); var $body = $('body');
$.each(specs, function(i, spec){ $.each(specs, function(i, spec){
if(spec[0] != "/"){ // if the spec isn't a plugin spec which means the spec file might be in a different subfolder
$body.append('<script src="specs/' + spec + '"></script>') $body.append('<script src="specs/' + spec + '"></script>')
}else{
$body.append('<script src="' + spec + '"></script>')
}
}); });
//initalize the test helper //initalize the test helper

View file

@ -100,8 +100,8 @@ describe("embed links", function(){
//open share dropdown //open share dropdown
chrome$(".buttonicon-embed").click(); chrome$(".buttonicon-embed").click();
//check read only checkbox, a bit hacky chrome$('#readonlyinput').click();
chrome$('#readonlyinput').attr('checked','checked').click().attr('checked','checked'); chrome$('#readonlyinput:checkbox:not(:checked)').attr('checked', 'checked');
//get the link of the share field + the actual pad url and compare them //get the link of the share field + the actual pad url and compare them
var shareLink = chrome$("#linkinput").val(); var shareLink = chrome$("#linkinput").val();
@ -119,7 +119,9 @@ describe("embed links", function(){
//open share dropdown //open share dropdown
chrome$(".buttonicon-embed").click(); chrome$(".buttonicon-embed").click();
//check read only checkbox, a bit hacky //check read only checkbox, a bit hacky
chrome$('#readonlyinput').attr('checked','checked').click().attr('checked','checked'); chrome$('#readonlyinput').click();
chrome$('#readonlyinput:checkbox:not(:checked)').attr('checked', 'checked');
//get the link of the share field + the actual pad url and compare them //get the link of the share field + the actual pad url and compare them
var embedCode = chrome$("#embedinput").val(); var embedCode = chrome$("#embedinput").val();
@ -129,5 +131,6 @@ describe("embed links", function(){
done(); done();
}); });
}); });
}); });
}); });

View file

@ -19,6 +19,7 @@ describe("font select", function(){
//select monospace and fire change event //select monospace and fire change event
$monospaceoption.attr('selected','selected'); $monospaceoption.attr('selected','selected');
$viewfontmenu.val("monospace");
$viewfontmenu.change(); $viewfontmenu.change();
//check if font changed to monospace //check if font changed to monospace

View file

@ -56,10 +56,8 @@ describe("Language select and change", function(){
//click the language button //click the language button
var $language = chrome$("#languagemenu"); var $language = chrome$("#languagemenu");
var $languageoption = $language.find("[value=en]"); //select english
$language.val("en");
//select german
$languageoption.attr('selected','selected');
$language.change(); $language.change();
//get the value of the bold button //get the value of the bold button

View file

@ -0,0 +1,64 @@
describe("timeslider", function(){
//create a new pad before each test run
beforeEach(function(cb){
helper.newPad(cb);
this.timeout(60000);
});
it("Shows a date and time in the timeslider and make sure it doesn't include NaN", function(done) {
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
// make some changes to produce 100 revisions
var revs = 10;
this.timeout(60000);
for(var i=0; i < revs; i++) {
setTimeout(function() {
// enter 'a' in the first text element
inner$("div").first().sendkeys('a');
}, 200);
}
setTimeout(function() {
// go to timeslider
$('#iframe-container iframe').attr('src', $('#iframe-container iframe').attr('src')+'/timeslider');
setTimeout(function() {
var timeslider$ = $('#iframe-container iframe')[0].contentWindow.$;
var $sliderBar = timeslider$('#ui-slider-bar');
var latestContents = timeslider$('#padcontent').text();
// Expect the date and time to be shown
// Click somewhere on the timeslider
var e = new jQuery.Event('mousedown');
e.clientX = e.pageX = 150;
e.clientY = e.pageY = 45;
$sliderBar.trigger(e);
e = new jQuery.Event('mousedown');
e.clientX = e.pageX = 150;
e.clientY = e.pageY = 40;
$sliderBar.trigger(e);
e = new jQuery.Event('mousedown');
e.clientX = e.pageX = 150;
e.clientY = e.pageY = 50;
$sliderBar.trigger(e);
$sliderBar.trigger('mouseup')
setTimeout(function() {
//make sure the text has changed
expect( timeslider$('#timer').text() ).not.to.eql( "" );
expect( timeslider$('#revision_date').text() ).not.to.eql( "" );
expect( timeslider$('#revision_label').text() ).not.to.eql( "" );
var includesNaN = timeslider$('#revision_label').text().indexOf("NaN"); // NaN is bad. Naan ist gut
expect( includesNaN ).to.eql( -1 ); // not quite so tasty, I like curry.
done();
}, 400);
}, 2000);
}, 2000);
});
});

View file

@ -4,8 +4,7 @@ describe("timeslider", function(){
helper.newPad(cb); helper.newPad(cb);
this.timeout(6000); this.timeout(6000);
}); });
it("loads adds a hundred revisions", function(done) {
xit("loads adds a hundred revisions", function(done) {
var inner$ = helper.padInner$; var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; var chrome$ = helper.padChrome$;
@ -57,14 +56,13 @@ describe("timeslider", function(){
}, 6000); }, 6000);
}, revs*timePerRev); }, revs*timePerRev);
}); });
it("changes the url when clicking on the timeslider", function(done) { it("changes the url when clicking on the timeslider", function(done) {
var inner$ = helper.padInner$; var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; var chrome$ = helper.padChrome$;
// make some changes to produce 7 revisions // make some changes to produce 7 revisions
var timePerRev = 900 var timePerRev = 1000
, revs = 7; , revs = 20;
this.timeout(revs*timePerRev+10000); this.timeout(revs*timePerRev+10000);
for(var i=0; i < revs; i++) { for(var i=0; i < revs; i++) {
setTimeout(function() { setTimeout(function() {
@ -100,28 +98,48 @@ describe("timeslider", function(){
}, 6000); }, 6000);
}, revs*timePerRev); }, revs*timePerRev);
}); });
it("jumps to a revision given in the url", function(done) { it("jumps to a revision given in the url", function(done) {
var inner$ = helper.padInner$; var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; var chrome$ = helper.padChrome$;
this.timeout(11000); this.timeout(20000);
// wait for the text to be loaded
helper.waitFor(function(){
return inner$('body').text().length != 0;
}, 6000).always(function() {
var newLines = inner$('body div').length;
var oldLength = inner$('body').text().length + newLines / 2;
expect( oldLength ).to.not.eql( 0 );
inner$("div").first().sendkeys('a'); inner$("div").first().sendkeys('a');
setTimeout(function() { // wait for our additional revision to be added
// go to timeslider
$('#iframe-container iframe').attr('src', $('#iframe-container iframe').attr('src')+'/timeslider#0');
var timeslider$;
helper.waitFor(function(){ helper.waitFor(function(){
timeslider$ = $('#iframe-container iframe')[0].contentWindow.$; // newLines takes the new lines into account which are strippen when using
return timeslider$ && timeslider$('#padcontent').text().length == 230; // inner$('body').text(), one <div> is used for one line in ACE.
var lenOkay = inner$('body').text().length + newLines / 2 != oldLength;
// this waits for the color to be added to our <span>, which means that the revision
// was accepted by the server.
var colorOkay = inner$('span').first().attr('class').indexOf("author-") == 0;
return lenOkay && colorOkay;
}, 6000).always(function() { }, 6000).always(function() {
expect( timeslider$('#padcontent').text().length ).to.eql( 230 ); // go to timeslider with a specific revision set
$('#iframe-container iframe').attr('src', $('#iframe-container iframe').attr('src')+'/timeslider#0');
// wait for the timeslider to be loaded
helper.waitFor(function(){
try {
timeslider$ = $('#iframe-container iframe')[0].contentWindow.$;
} catch(e){}
if(timeslider$){
return timeslider$('#padcontent').text().length == oldLength;
}
}, 6000).always(function(){
expect( timeslider$('#padcontent').text().length ).to.eql( oldLength );
done(); done();
}); });
}, 2500);
}); });
});
});
it("checks the export url", function(done) { it("checks the export url", function(done) {
var inner$ = helper.padInner$; var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; var chrome$ = helper.padChrome$;
@ -135,7 +153,9 @@ describe("timeslider", function(){
var exportLink; var exportLink;
helper.waitFor(function(){ helper.waitFor(function(){
try{
timeslider$ = $('#iframe-container iframe')[0].contentWindow.$; timeslider$ = $('#iframe-container iframe')[0].contentWindow.$;
}catch(e){}
if(!timeslider$) if(!timeslider$)
return false; return false;
exportLink = timeslider$('#exportplaina').attr('href'); exportLink = timeslider$('#exportplaina').attr('href');