mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-04-23 00:46:16 -04:00
Merge branch 'release/1.2.8'
This commit is contained in:
commit
cb2d148734
95 changed files with 7555 additions and 6982 deletions
36
CHANGELOG.md
36
CHANGELOG.md
|
@ -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
|
||||
* 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
|
||||
|
|
|
@ -28,7 +28,7 @@ documented codebase makes it easier for developers to improve the code and contr
|
|||
|
||||
|
||||
Etherpad Lite is designed to be easily embeddable and provides a [HTTP API](https://github.com/ether/etherpad-lite/wiki/HTTP-API)
|
||||
that allows your web application to manage pads, users and groups. It is recommended to use the [available client implementations](https://github.com/ether/etherpad-lite/wiki/HTTP-API-client-libraries) in order to interact with this API. There is also a [jQuery plugin](https://github.com/johnyma22/etherpad-lite-jquery-plugin) that helps you to embed Pads into your website.
|
||||
that allows your web application to manage pads, users and groups. It is recommended to use the [available client implementations](https://github.com/ether/etherpad-lite/wiki/HTTP-API-client-libraries) in order to interact with this API. There is also a [jQuery plugin](https://github.com/ether/etherpad-lite-jquery-plugin) that helps you to embed Pads into your website.
|
||||
There's also a full-featured plugin framework, allowing you to easily add your own features.
|
||||
Finally, Etherpad Lite comes with translations into tons of different languages!
|
||||
|
||||
|
@ -108,7 +108,7 @@ You know all this and just want to know how you can help?
|
|||
|
||||
Look at the [TODO list](https://github.com/ether/etherpad-lite/wiki/TODO) and our [Issue tracker](https://github.com/ether/etherpad-lite/issues). (Please consider using [jshint](http://www.jshint.com/about/), if you plan to contribute code.)
|
||||
|
||||
Also, and most importantly, read our [**Developer Guidelines**](https://github.com/ether/etherpad-lite/wiki/Developer-Guidelines), really!
|
||||
Also, and most importantly, read our [**Developer Guidelines**](https://github.com/ether/etherpad-lite/blob/master/CONTRIBUTING.md), really!
|
||||
|
||||
# Get in touch
|
||||
Join the [mailinglist](http://groups.google.com/group/etherpad-lite-dev) and make some noise on our freenode irc channel [#etherpad-lite-dev](http://webchat.freenode.net?channels=#etherpad-lite-dev)!
|
||||
|
@ -122,6 +122,7 @@ Join the [mailinglist](http://groups.google.com/group/etherpad-lite-dev) and mak
|
|||
# Donate!
|
||||
* [Flattr] (http://flattr.com/thing/71378/Etherpad-Foundation)
|
||||
* Paypal - Press the donate button on [etherpad.org](http://etherpad.org)
|
||||
* [Bitcoin] (https://coinbase.com/checkouts/1e572bf8a82e4663499f7f1f66c2d15a)
|
||||
|
||||
# License
|
||||
[Apache License v2](http://www.apache.org/licenses/LICENSE-2.0.html)
|
||||
|
|
63
bin/deletePad.js
Normal file
63
bin/deletePad.js
Normal 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();
|
||||
}
|
||||
});
|
|
@ -13,8 +13,8 @@ var padId = process.argv[2];
|
|||
var db, dirty, padManager, pad, settings;
|
||||
var neededDBValues = ["pad:"+padId];
|
||||
|
||||
var npm = require("../src/node_modules/npm");
|
||||
var async = require("../src/node_modules/async");
|
||||
var npm = require("../node_modules/ep_etherpad-lite/node_modules/npm");
|
||||
var async = require("../node_modules/ep_etherpad-lite/node_modules/async");
|
||||
|
||||
async.series([
|
||||
// load npm
|
||||
|
@ -33,9 +33,10 @@ async.series([
|
|||
},
|
||||
// load modules
|
||||
function(callback) {
|
||||
settings = require('../src/node/utils/Settings');
|
||||
db = require('../src/node/db/DB');
|
||||
dirty = require("../src/node_modules/ueberDB/node_modules/dirty")(padId + ".db");
|
||||
settings = require('../node_modules/ep_etherpad-lite/node/utils/Settings');
|
||||
db = require('../node_modules/ep_etherpad-lite/node/db/DB');
|
||||
dirty = require("../node_modules/ep_etherpad-lite/node_modules/ueberDB/node_modules/dirty")(padId + ".db");
|
||||
callback();
|
||||
},
|
||||
//intallize the database
|
||||
function (callback)
|
||||
|
@ -45,7 +46,7 @@ async.series([
|
|||
//get the pad
|
||||
function (callback)
|
||||
{
|
||||
padManager = require('../node/db/PadManager');
|
||||
padManager = require('../node_modules/ep_etherpad-lite/node/db/PadManager');
|
||||
|
||||
padManager.getPad(padId, function(err, _pad)
|
||||
{
|
||||
|
@ -82,7 +83,10 @@ async.series([
|
|||
db.db.db.wrappedDB.get(dbkey, function(err, dbvalue)
|
||||
{
|
||||
if(err) { callback(err); return}
|
||||
dbvalue=JSON.parse(dbvalue);
|
||||
|
||||
if(dbvalue && typeof dbvalue != 'object'){
|
||||
dbvalue=JSON.parse(dbvalue); // if its not json then parse it as json
|
||||
}
|
||||
|
||||
dirty.set(dbkey, dbvalue, callback);
|
||||
});
|
||||
|
|
|
@ -63,7 +63,7 @@ if [ ! -f $settings ]; then
|
|||
cp settings.json.template $settings || exit 1
|
||||
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
|
||||
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..."
|
||||
DOWNLOAD_JQUERY="true"
|
||||
NEEDED_VERSION="1.7.1"
|
||||
NEEDED_VERSION="1.9.1"
|
||||
if [ -f "src/static/js/jquery.js" ]; then
|
||||
if [ $(uname) = "SunOS" ]; then
|
||||
VERSION=$(cat src/static/js/jquery.js | head -n 3 | ggrep -o "v[0-9]\.[0-9]\(\.[0-9]\)\?");
|
||||
|
|
|
@ -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
|
||||
|
||||
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
|
||||
|
||||
echo _
|
||||
|
@ -36,4 +36,4 @@ IF NOT EXIST settings.json (
|
|||
)
|
||||
|
||||
echo _
|
||||
echo Installed Etherpad-lite! To run Etherpad type start.bat
|
||||
echo Installed Etherpad! To run Etherpad type start.bat
|
||||
|
|
|
@ -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.
|
||||
|
||||
TODO:
|
||||
|
|
|
@ -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.
|
||||
|
||||
## 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
|
||||
Called from: src/static/js/pad_userlist.js
|
||||
|
||||
|
|
|
@ -61,7 +61,9 @@ Portal submits content into new blog post
|
|||
## Usage
|
||||
|
||||
### 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
|
||||
|
||||
|
|
|
@ -104,4 +104,16 @@ Your plugin must also contain a [package definition file](http://npmjs.org/doc/j
|
|||
```
|
||||
|
||||
## 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.
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
{
|
||||
// Name your instance!
|
||||
"title": "Etherpad Lite",
|
||||
"title": "Etherpad",
|
||||
|
||||
// favicon default name
|
||||
// alternatively, set up a fully specified Url to your own favicon
|
||||
|
@ -15,6 +15,10 @@
|
|||
"ip": "0.0.0.0",
|
||||
"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
|
||||
// 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
|
||||
"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. */
|
||||
"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 */
|
||||
"loglevel": "INFO",
|
||||
|
||||
// restrict socket.io transport methods
|
||||
"socketTransportProtocols" : ["xhr-polling", "jsonp-polling", "htmlfile"]
|
||||
|
||||
//Logging configuration. See log4js documentation for further information
|
||||
// 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
1
src/README.md
Normal file
|
@ -0,0 +1 @@
|
|||
Ignore this file and see the file in the base installation folder
|
|
@ -2,26 +2,60 @@
|
|||
"@metadata": {
|
||||
"authors": [
|
||||
"Bellayet",
|
||||
"Nasir8891"
|
||||
"Nasir8891",
|
||||
"Sankarshan"
|
||||
]
|
||||
},
|
||||
"index.newPad": "\u09a8\u09a4\u09c1\u09a8 \u09aa\u09cd\u09af\u09be\u09a1",
|
||||
"index.createOpenPad": "\u0985\u09a5\u09ac\u09be \u09a8\u09be\u09ae \u09b2\u09bf\u0996\u09c7 \u09aa\u09cd\u09af\u09be\u09a1 \u0996\u09c1\u09b2\u09c1\u09a8\/\u09a4\u09c8\u09b0\u09c0 \u0995\u09b0\u09c1\u09a8:",
|
||||
"pad.toolbar.bold.title": "\u0997\u09be\u09a1\u09bc \u0995\u09b0\u09be (Ctrl-B)",
|
||||
"pad.toolbar.italic.title": "\u09ac\u09be\u0981\u0995\u09be \u0995\u09b0\u09be (Ctrl-I)",
|
||||
"pad.toolbar.underline.title": "\u0986\u09a8\u09cd\u09a1\u09be\u09b0\u09b2\u09be\u0987\u09a8 (Ctrl-U)",
|
||||
"pad.toolbar.ol.title": "\u09b8\u09be\u09b0\u09bf\u09ac\u09a6\u09cd\u09a7 \u09a4\u09be\u09b2\u09bf\u0995\u09be",
|
||||
"pad.toolbar.indent.title": "\u09aa\u09cd\u09b0\u09be\u09a8\u09cd\u09a4\u09bf\u0995\u0995\u09b0\u09a3",
|
||||
"pad.toolbar.unindent.title": "\u0986\u0989\u099f\u09a1\u09c7\u09a8\u09cd\u099f",
|
||||
"pad.toolbar.undo.title": "\u09ac\u09be\u09a4\u09bf\u09b2 \u0995\u09b0\u09c1\u09a8 (Ctrl-Z)",
|
||||
"pad.toolbar.redo.title": "\u09aa\u09c1\u09a8\u09b0\u09be\u09af\u09bc \u0995\u09b0\u09c1\u09a8 (Ctrl-Y)",
|
||||
"pad.toolbar.clearAuthorship.title": "\u0995\u09c3\u09a4\u09bf \u09b0\u0982 \u09aa\u09b0\u09bf\u09b7\u09cd\u0995\u09be\u09b0 \u0995\u09b0\u09c1\u09a8",
|
||||
"pad.toolbar.timeslider.title": "\u099f\u09be\u0987\u09ae\u09b8\u09cd\u09b2\u09be\u0987\u09a1\u09be\u09b0",
|
||||
"pad.toolbar.savedRevision.title": "\u09b8\u0982\u09b8\u09cd\u0995\u09b0\u09a3 \u09b8\u0982\u09b0\u0995\u09cd\u09b7\u09a3 \u0995\u09b0\u09c1\u09a8",
|
||||
"pad.toolbar.settings.title": "\u09b8\u09c7\u099f\u09bf\u0982",
|
||||
"pad.toolbar.embed.title": "\u098f\u0987 \u09aa\u09cd\u09af\u09be\u09a1-\u099f\u09bf \u098f\u09ae\u09cd\u09ac\u09c7\u09a1 \u0995\u09b0\u09c1\u09a8",
|
||||
"pad.toolbar.showusers.title": "\u098f\u0987 \u09aa\u09cd\u09af\u09be\u09a1\u09c7\u09b0 \u09ac\u09cd\u09af\u09ac\u09b9\u09be\u09b0\u0995\u09be\u09b0\u09c0\u09a6\u09c7\u09b0 \u09a6\u09c7\u0996\u09be\u09a8",
|
||||
"pad.colorpicker.save": "\u09b8\u0982\u09b0\u0995\u09cd\u09b7\u09a3",
|
||||
"pad.colorpicker.cancel": "\u09ac\u09be\u09a4\u09bf\u09b2",
|
||||
"pad.loading": "\u09b2\u09cb\u09a1\u09bf\u0982...",
|
||||
"pad.passwordRequired": "\u098f\u0987 \u09aa\u09cd\u09af\u09be\u09a1-\u099f\u09bf \u09a6\u09c7\u0996\u09be\u09b0 \u099c\u09a8\u09cd\u09af \u0986\u09aa\u09a8\u09be\u0995\u09c7 \u09aa\u09be\u09b8\u0993\u09af\u09bc\u09be\u09b0\u09cd\u09a1 \u09ac\u09cd\u09af\u09ac\u09b9\u09be\u09b0 \u0995\u09b0\u09a4\u09c7 \u09b9\u09ac\u09c7",
|
||||
"pad.permissionDenied": "\u09a6\u09c1\u0983\u0996\u09bf\u09a4, \u098f \u09aa\u09cd\u09af\u09be\u09a1-\u099f\u09bf \u09a6\u09c7\u0996\u09be\u09b0 \u0985\u09a7\u09bf\u0995\u09be\u09b0 \u0986\u09aa\u09a8\u09be\u09b0 \u09a8\u09c7\u0987",
|
||||
"pad.wrongPassword": "\u0986\u09aa\u09a8\u09be\u09b0 \u09aa\u09be\u09b8\u0993\u09af\u09bc\u09be\u09b0\u09cd\u09a1 \u09b8\u09a0\u09bf\u0995 \u09a8\u09af\u09bc",
|
||||
"pad.settings.padSettings": "\u09aa\u09cd\u09af\u09be\u09a1\u09c7\u09b0 \u09b8\u09cd\u09a5\u09be\u09aa\u09a8",
|
||||
"pad.settings.myView": "\u0986\u09ae\u09be\u09b0 \u09a6\u09c3\u09b6\u09cd\u09af",
|
||||
"pad.settings.stickychat": "\u099a\u09cd\u09af\u09be\u099f \u09b8\u0995\u09cd\u09b0\u09c0\u09a8\u09c7 \u09aa\u09cd\u09b0\u09a6\u09b0\u09cd\u09b6\u09a8 \u0995\u09b0\u09be \u09b9\u09ac\u09c7",
|
||||
"pad.settings.colorcheck": "\u09b2\u09c7\u0996\u0995\u09a6\u09c7\u09b0 \u09a8\u09bf\u099c\u09b8\u09cd\u09ac \u09a8\u09bf\u09b0\u09cd\u09ac\u09be\u099a\u09bf\u09a4 \u09b0\u0982",
|
||||
"pad.settings.linenocheck": "\u09b2\u09be\u0987\u09a8 \u09a8\u09ae\u09cd\u09ac\u09b0",
|
||||
"pad.settings.fontType": "\u09ab\u09a8\u09cd\u099f-\u098f\u09b0 \u09aa\u09cd\u09b0\u0995\u09be\u09b0:",
|
||||
"pad.settings.fontType.normal": "\u09b8\u09be\u09a7\u09be\u09b0\u09a3",
|
||||
"pad.settings.fontType.monospaced": "Monospace",
|
||||
"pad.settings.globalView": "\u09b8\u09b0\u09cd\u09ac\u09ac\u09cd\u09af\u09be\u09aa\u09c0 \u09a6\u09c3\u09b6\u09cd\u09af",
|
||||
"pad.settings.language": "\u09ad\u09be\u09b7\u09be:",
|
||||
"pad.importExport.import_export": "\u0987\u09ae\u09cd\u09aa\u09cb\u09b0\u099f\/\u098f\u0995\u09cd\u09b8\u09aa\u09cb\u09b0\u09cd\u099f",
|
||||
"pad.importExport.import": "\u0995\u09cb\u09a8 \u099f\u09c7\u0995\u09cd\u09b8\u099f \u09ab\u09be\u0987\u09b2 \u09ac\u09be \u09a1\u0995\u09c1\u09ae\u09c7\u09a8\u09cd\u099f \u0986\u09aa\u09b2\u09cb\u09a1 \u0995\u09b0\u09c1\u09a8",
|
||||
"pad.importExport.importSuccessful": "\u09b8\u09ab\u09b2!",
|
||||
"pad.importExport.export": "\u098f\u0987 \u09aa\u09cd\u09af\u09be\u09a1\u099f\u09bf \u098f\u0995\u09cd\u09b8\u09aa\u09cb\u09b0\u09cd\u099f \u0995\u09b0\u09c1\u09a8",
|
||||
"pad.importExport.exporthtml": "\u098f\u0987\u099a\u099f\u09bf\u098f\u09ae\u098f\u09b2",
|
||||
"pad.importExport.exportplain": "\u09b8\u09be\u09a7\u09be\u09b0\u09a3 \u09b2\u09c7\u0996\u09be",
|
||||
"pad.importExport.exportword": "\u09ae\u09be\u0987\u0995\u09cd\u09b0\u09cb\u09b8\u09ab\u099f \u0993\u09af\u09bc\u09be\u09b0\u09cd\u09a1",
|
||||
"pad.importExport.exportpdf": "\u09aa\u09bf\u09a1\u09bf\u098f\u09ab",
|
||||
"pad.importExport.exportopen": "\u0993\u09a1\u09bf\u098f\u09ab (\u0993\u09aa\u09c7\u09a8 \u09a1\u0995\u09c1\u09ae\u09c7\u09a8\u09cd\u099f \u09ab\u09b0\u09ae\u09cd\u09af\u09be\u099f)",
|
||||
"pad.importExport.exportdokuwiki": "DokuWiki",
|
||||
"pad.modals.connected": "\u09af\u09cb\u0997\u09be\u09af\u09cb\u0997 \u09b8\u09ab\u09b2",
|
||||
"pad.modals.reconnecting": "\u0986\u09aa\u09a8\u09be\u09b0 \u09aa\u09cd\u09af\u09be\u09a1\u09c7\u09b0 \u09b8\u09be\u09a5\u09c7 \u09b8\u0982\u09af\u09cb\u0997\u09b8\u09cd\u09a5\u09be\u09aa\u09a8 \u0995\u09b0\u09be \u09b9\u099a\u09cd\u099b\u09c7..",
|
||||
"pad.modals.forcereconnect": "\u09aa\u09c1\u09a8\u09b0\u09be\u09af\u09bc \u09b8\u0982\u09af\u09cb\u0997\u09b8\u09cd\u09a5\u09be\u09aa\u09a8\u09c7\u09b0 \u099a\u09c7\u09b7\u09cd\u099f\u09be",
|
||||
"pad.modals.userdup": "\u0985\u09a8\u09cd\u09af \u0989\u0987\u09a8\u09cd\u09a1\u09cb-\u09a4\u09c7 \u0996\u09cb\u09b2\u09be \u09b9\u09af\u09bc\u09c7\u099b\u09c7",
|
||||
"pad.modals.unauth": "\u0986\u09aa\u09a8\u09be\u09b0 \u0985\u09a7\u09bf\u0995\u09be\u09b0 \u09a8\u09c7\u0987",
|
||||
"pad.modals.looping": "\u09af\u09cb\u0997\u09be\u09af\u09cb\u0997 \u09ac\u09bf\u099a\u09cd\u099b\u09bf\u09a8\u09cd\u09a8",
|
||||
"pad.modals.initsocketfail": "\u09b8\u09be\u09b0\u09cd\u09ad\u09be\u09b0-\u098f\u09b0 \u09b8\u09be\u09a5\u09c7 \u09af\u09cb\u0997\u09be\u09af\u09cb\u0997 \u0995\u09b0\u09a4\u09c7 \u0985\u09b8\u0995\u09cd\u09b7\u09ae\u0964",
|
||||
"pad.modals.slowcommit": "\u09af\u09cb\u0997\u09be\u09af\u09cb\u0997 \u09ac\u09bf\u099a\u09cd\u099b\u09bf\u09a8\u09cd\u09a8",
|
||||
"pad.modals.deleted": "\u0985\u09aa\u09b8\u09be\u09b0\u09bf\u09a4\u0964",
|
||||
"pad.modals.deleted.explanation": "\u098f\u0987 \u09aa\u09cd\u09af\u09be\u09a1\u099f\u09bf \u0985\u09aa\u09b8\u09be\u09b0\u09a3 \u0995\u09b0\u09be \u09b9\u09af\u09bc\u09c7\u099b\u09c7\u0964",
|
||||
"pad.modals.disconnected.explanation": "\u09b8\u09be\u09b0\u09cd\u09ad\u09be\u09b0\u09c7\u09b0 \u09b8\u09be\u09a5\u09c7 \u09af\u09cb\u0997\u09be\u09af\u09cb\u0997 \u0995\u09b0\u09be \u09af\u09be\u099a\u09cd\u099b\u09c7 \u09a8\u09be",
|
||||
|
@ -34,6 +68,7 @@
|
|||
"timeslider.toolbar.authors": "\u09b2\u09c7\u0996\u0995\u0997\u09a3:",
|
||||
"timeslider.toolbar.authorsList": "\u0995\u09cb\u09a8\u09cb \u09b2\u09c7\u0996\u0995 \u09a8\u09c7\u0987",
|
||||
"timeslider.exportCurrent": "\u09ac\u09b0\u09cd\u09a4\u09ae\u09be\u09a8 \u09b8\u0982\u09b8\u09cd\u0995\u09b0\u09a3\u099f\u09bf \u098f\u0995\u09cd\u09b8\u09aa\u09cb\u09b0\u09cd\u099f \u0995\u09b0\u09c1\u09a8:",
|
||||
"timeslider.dateformat": "{{month}}\/{{day}}\/{{year}} {{hours}}:{{minutes}}:{{seconds}}",
|
||||
"timeslider.month.january": "\u099c\u09be\u09a8\u09c1\u09af\u09bc\u09be\u09b0\u09bf",
|
||||
"timeslider.month.february": "\u09ab\u09c7\u09ac\u09cd\u09b0\u09c1\u09af\u09bc\u09be\u09b0\u09bf",
|
||||
"timeslider.month.march": "\u09ae\u09be\u09b0\u09cd\u099a",
|
||||
|
@ -45,5 +80,12 @@
|
|||
"timeslider.month.september": "\u09b8\u09c7\u09aa\u09cd\u099f\u09c7\u09ae\u09cd\u09ac\u09b0",
|
||||
"timeslider.month.october": "\u0985\u0995\u09cd\u099f\u09cb\u09ac\u09b0",
|
||||
"timeslider.month.november": "\u09a8\u09ad\u09c7\u09ae\u09cd\u09ac\u09b0",
|
||||
"timeslider.month.december": "\u09a1\u09bf\u09b8\u09c7\u09ae\u09cd\u09ac\u09b0"
|
||||
"timeslider.month.december": "\u09a1\u09bf\u09b8\u09c7\u09ae\u09cd\u09ac\u09b0",
|
||||
"pad.userlist.entername": "\u0986\u09aa\u09a8\u09be\u09b0 \u09a8\u09be\u09ae",
|
||||
"pad.userlist.unnamed": "\u0995\u09cb\u09a8 \u09a8\u09be\u09ae \u09a8\u09bf\u09b0\u09cd\u09ac\u09be\u099a\u09a8 \u0995\u09b0\u09be \u09b9\u09af\u09bc\u09a8\u09bf",
|
||||
"pad.userlist.guest": "\u0985\u09a4\u09bf\u09a5\u09bf",
|
||||
"pad.userlist.approve": "\u0985\u09a8\u09c1\u09ae\u09cb\u09a6\u09bf\u09a4",
|
||||
"pad.impexp.importbutton": "\u098f\u0996\u09a8 \u0987\u09ae\u09cd\u09aa\u09cb\u09b0\u09cd\u099f \u0995\u09b0\u09c1\u09a8",
|
||||
"pad.impexp.importing": "\u0987\u09ae\u09cd\u09aa\u09cb\u09b0\u09cd\u099f \u099a\u09b2\u099b\u09c7...",
|
||||
"pad.impexp.importfailed": "\u0987\u09ae\u09cd\u09aa\u09cb\u09b0\u09cd\u099f \u0985\u09b8\u0995\u09cd\u09b7\u09ae"
|
||||
}
|
123
src/locales/br.json
Normal file
123
src/locales/br.json
Normal 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."
|
||||
}
|
|
@ -17,7 +17,7 @@
|
|||
"pad.toolbar.undo.title": "Desf\u00e9s (Ctrl-Z)",
|
||||
"pad.toolbar.redo.title": "Ref\u00e9s (Ctrl-Y)",
|
||||
"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.showusers.title": "Mostra els usuaris d\u2019aquest pad",
|
||||
"pad.colorpicker.save": "Desa",
|
||||
|
@ -25,18 +25,25 @@
|
|||
"pad.loading": "S'est\u00e0 carregant...",
|
||||
"pad.wrongPassword": "La contrasenya \u00e9s incorrecta",
|
||||
"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.fontType": "Tipus de lletra:",
|
||||
"pad.settings.fontType.normal": "Normal",
|
||||
"pad.settings.fontType.monospaced": "D'amplada fixa",
|
||||
"pad.settings.globalView": "Vista global",
|
||||
"pad.settings.language": "Llengua:",
|
||||
"pad.importExport.import_export": "Importaci\u00f3\/exportaci\u00f3",
|
||||
"pad.importExport.import": "Puja qualsevol fitxer de text o document",
|
||||
"pad.importExport.exporthtml": "HTML",
|
||||
"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.forcereconnect": "For\u00e7a tornar a connectar",
|
||||
"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.initsocketfail": "El servidor no \u00e9s accessible.",
|
||||
"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.deleted": "Suprimit.",
|
||||
"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.link": "Enlla\u00e7",
|
||||
"pad.chat": "Xat",
|
||||
|
@ -52,6 +60,8 @@
|
|||
"timeslider.toolbar.exportlink.title": "Exporta",
|
||||
"timeslider.exportCurrent": "Exporta la versi\u00f3 actual com a:",
|
||||
"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.february": "Febrer",
|
||||
"timeslider.month.march": "Mar\u00e7",
|
||||
|
@ -69,9 +79,11 @@
|
|||
"pad.userlist.guest": "Convidat",
|
||||
"pad.userlist.deny": "Refusa",
|
||||
"pad.userlist.approve": "Aprova",
|
||||
"pad.editbar.clearcolors": "Voleu netejar els colors d'autor del document sencer?",
|
||||
"pad.impexp.importbutton": "Importa ara",
|
||||
"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.uploadFailed": "Ha fallat la c\u00e0rrega. Torneu-ho a provar",
|
||||
"pad.impexp.importfailed": "Ha fallat la importaci\u00f3",
|
||||
"pad.impexp.copypaste": "Si us plau, copieu i enganxeu"
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Christian List"
|
||||
"Christian List",
|
||||
"Peter Alberti"
|
||||
]
|
||||
},
|
||||
"index.newPad": "Ny Pad",
|
||||
|
@ -100,6 +101,8 @@
|
|||
"timeslider.month.october": "oktober",
|
||||
"timeslider.month.november": "november",
|
||||
"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.userlist.entername": "Indtast dit navn",
|
||||
"pad.userlist.unnamed": "ikke-navngivet",
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
"pad.toolbar.clearAuthorship.title": "Autorenfarben zur\u00fccksetzen",
|
||||
"pad.toolbar.import_export.title": "Import\/Export in verschiedenen Dateiformaten",
|
||||
"pad.toolbar.timeslider.title": "Pad-Versionsgeschichte anzeigen",
|
||||
"pad.toolbar.savedRevision.title": "Diese Revision markieren",
|
||||
"pad.toolbar.savedRevision.title": "Version speichern",
|
||||
"pad.toolbar.settings.title": "Einstellungen",
|
||||
"pad.toolbar.embed.title": "Dieses Pad teilen oder einbetten",
|
||||
"pad.toolbar.showusers.title": "Aktuell verbundene Benutzer anzeigen",
|
||||
|
|
|
@ -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.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.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.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",
|
||||
|
@ -81,6 +81,7 @@
|
|||
"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.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.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:",
|
||||
|
@ -88,7 +89,7 @@
|
|||
"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.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.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",
|
||||
|
@ -102,6 +103,8 @@
|
|||
"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.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.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",
|
||||
|
|
|
@ -4,8 +4,9 @@
|
|||
"0": "Armando-Martin",
|
||||
"1": "Jacobo",
|
||||
"2": "Joker",
|
||||
"4": "Vivaelcelta",
|
||||
"5": "Xuacu"
|
||||
"3": "Rubenwap",
|
||||
"5": "Vivaelcelta",
|
||||
"6": "Xuacu"
|
||||
}
|
||||
},
|
||||
"index.newPad": "Nuevo Pad",
|
||||
|
@ -104,6 +105,8 @@
|
|||
"timeslider.month.october": "Octubre",
|
||||
"timeslider.month.november": "Noviembre",
|
||||
"timeslider.month.december": "Diciembre",
|
||||
"timeslider.unnamedauthor": "{{num}} autor desconocido",
|
||||
"timeslider.unnamedauthors": "{{num}} autores desconocidos",
|
||||
"pad.savedrevs.marked": "Revisi\u00f3n guardada",
|
||||
"pad.userlist.entername": "Escribe tu nombre",
|
||||
"pad.userlist.unnamed": "an\u00f3nimo",
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
"pad.toolbar.clearAuthorship.title": "Poista kirjoittajav\u00e4rit",
|
||||
"pad.toolbar.import_export.title": "Tuo tai vie eri tiedostomuodoista tai -muotoihin",
|
||||
"pad.toolbar.timeslider.title": "Aikajana",
|
||||
"pad.toolbar.savedRevision.title": "Tallennetut versiot",
|
||||
"pad.toolbar.savedRevision.title": "Tallenna muutos",
|
||||
"pad.toolbar.settings.title": "Asetukset",
|
||||
"pad.toolbar.embed.title": "Upota muistio",
|
||||
"pad.toolbar.showusers.title": "N\u00e4yt\u00e4 muistion k\u00e4ytt\u00e4j\u00e4t",
|
||||
|
@ -105,6 +105,8 @@
|
|||
"timeslider.month.october": "lokakuu",
|
||||
"timeslider.month.november": "marraskuu",
|
||||
"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.userlist.entername": "Kirjoita nimesi",
|
||||
"pad.userlist.unnamed": "nimet\u00f6n",
|
||||
|
|
|
@ -109,6 +109,8 @@
|
|||
"timeslider.month.october": "Octobre",
|
||||
"timeslider.month.november": "Novembre",
|
||||
"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.userlist.entername": "Entrez votre nom",
|
||||
"pad.userlist.unnamed": "sans nom",
|
||||
|
|
|
@ -100,6 +100,8 @@
|
|||
"timeslider.month.october": "outubro",
|
||||
"timeslider.month.november": "novembro",
|
||||
"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.userlist.entername": "Insira o seu nome",
|
||||
"pad.userlist.unnamed": "an\u00f3nimo",
|
||||
|
|
|
@ -79,6 +79,7 @@
|
|||
"pad.share.emebdcode": "\u05d4\u05d8\u05de\u05e2\u05ea \u05e7\u05d9\u05e9\u05d5\u05e8",
|
||||
"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.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.toolbar.returnbutton": "\u05d7\u05d6\u05e8\u05d4 \u05d0\u05dc \u05d4\u05e4\u05e0\u05e7\u05e1",
|
||||
"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.november": "\u05e0\u05d5\u05d1\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.userlist.entername": "\u05e0\u05d0 \u05dc\u05d4\u05d6\u05d9\u05df \u05d0\u05ea \u05e9\u05de\u05da",
|
||||
"pad.userlist.unnamed": "\u05dc\u05dc\u05d0 \u05e9\u05dd",
|
||||
|
|
|
@ -103,6 +103,8 @@
|
|||
"timeslider.month.october": "ottobre",
|
||||
"timeslider.month.november": "novembre",
|
||||
"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.userlist.entername": "Inserisci il tuo nome",
|
||||
"pad.userlist.unnamed": "senza nome",
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
"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.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.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",
|
||||
|
@ -87,7 +87,7 @@
|
|||
"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.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.february": "2\u6708",
|
||||
"timeslider.month.march": "3\u6708",
|
||||
|
@ -100,6 +100,8 @@
|
|||
"timeslider.month.october": "10\u6708",
|
||||
"timeslider.month.november": "11\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.userlist.entername": "\u540d\u524d\u3092\u5165\u529b",
|
||||
"pad.userlist.unnamed": "\u540d\u524d\u306a\u3057",
|
||||
|
|
|
@ -100,6 +100,8 @@
|
|||
"timeslider.month.october": "10\uc6d4",
|
||||
"timeslider.month.november": "11\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.userlist.entername": "\uc774\ub984\uc744 \uc785\ub825\ud558\uc138\uc694",
|
||||
"pad.userlist.unnamed": "\uc774\ub984\uc5c6\uc74c",
|
||||
|
|
|
@ -19,13 +19,16 @@
|
|||
"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.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.embed.title": "Donn dat Padd enbenge",
|
||||
"pad.toolbar.showusers.title": "Verbonge Metschriiver aanzeije",
|
||||
"pad.colorpicker.save": "Fa\u00dfhallde",
|
||||
"pad.colorpicker.cancel": "Oph\u00fc\u00fcre",
|
||||
"pad.loading": "Aam Laade …",
|
||||
"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.myView": "Anseesch",
|
||||
"pad.settings.stickychat": "Donn der Klaaf emmer aanzeije",
|
||||
|
@ -38,6 +41,7 @@
|
|||
"pad.settings.language": "Schprooch:",
|
||||
"pad.importExport.import_export": "Empoot\/\u00c4xpoot",
|
||||
"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.exporthtml": "HTML",
|
||||
"pad.importExport.exportplain": "Eijfach T\u00e4x",
|
||||
|
@ -45,9 +49,11 @@
|
|||
"pad.importExport.exportpdf": "PDF (Poteerbaa Dokem\u00e4nte Fommaat)",
|
||||
"pad.importExport.exportopen": "ODF (Offe Dokem\u00e4nte-Fommaat)",
|
||||
"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.reconnecting": "Ben wider aam 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.advice": "En heh d\u00e4m Finster wider verbenge.",
|
||||
"pad.modals.unauth": "Nit ber\u00e4\u00e4schtesch",
|
||||
|
@ -72,10 +78,12 @@
|
|||
"pad.share.emebdcode": "URL enboue",
|
||||
"pad.chat": "Klaaf",
|
||||
"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.toolbar.returnbutton": "Jangk retuur nohm P\u00e4dd",
|
||||
"timeslider.toolbar.authors": "Schriiver:",
|
||||
"timeslider.toolbar.authorsList": "Kein Schriivere",
|
||||
"timeslider.toolbar.exportlink.title": "\u00c4xpoot",
|
||||
"timeslider.exportCurrent": "Donn de meu\u00dfte V\u00e4sjohn \u00e4xpotteere al\u00df:",
|
||||
"timeslider.version": "V\u00e4sjon {{version}}",
|
||||
"timeslider.saved": "Fa\u00dfjehallde aam {{day}}. {{month}} {{year}}",
|
||||
|
@ -92,11 +100,19 @@
|
|||
"timeslider.month.october": "Oktoober",
|
||||
"timeslider.month.november": "Nov\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.unnamed": "naamelo\u00df\u00df",
|
||||
"pad.userlist.guest": "Ja\u00df\u00df",
|
||||
"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 …",
|
||||
"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.importfailed": "Et Empoteere es don\u00e4vve jejange",
|
||||
"pad.impexp.copypaste": "Bes esu jood un donn et koppeere un enf\u00f6\u00f6je",
|
||||
|
|
|
@ -101,6 +101,8 @@
|
|||
"timeslider.month.october": "\u043e\u043a\u0442\u043e\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.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.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",
|
||||
|
|
|
@ -79,6 +79,7 @@
|
|||
"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.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.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:",
|
||||
|
|
|
@ -78,6 +78,7 @@
|
|||
"pad.share.emebdcode": "Benamkan URL",
|
||||
"pad.chat": "Sembang",
|
||||
"pad.chat.title": "Buka ruang sembang untuk pad ini.",
|
||||
"pad.chat.loadmessages": "Muatkan banyak lagi pesanan",
|
||||
"timeslider.pageTitle": "Gelangsar Masa {{appTitle}}",
|
||||
"timeslider.toolbar.returnbutton": "Kembali ke pad",
|
||||
"timeslider.toolbar.authors": "Pengarang:",
|
||||
|
@ -99,6 +100,8 @@
|
|||
"timeslider.month.october": "Oktober",
|
||||
"timeslider.month.november": "November",
|
||||
"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.userlist.entername": "Taipkan nama anda",
|
||||
"pad.userlist.unnamed": "tanpa nama",
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
"pad.toolbar.clearAuthorship.title": "Kleuren auteurs wissen",
|
||||
"pad.toolbar.import_export.title": "Naar\/van andere opmaak exporteren\/importeren",
|
||||
"pad.toolbar.timeslider.title": "Tijdlijn",
|
||||
"pad.toolbar.savedRevision.title": "Opgeslagen versies",
|
||||
"pad.toolbar.savedRevision.title": "Versie opslaan",
|
||||
"pad.toolbar.settings.title": "Instellingen",
|
||||
"pad.toolbar.embed.title": "Pad insluiten",
|
||||
"pad.toolbar.showusers.title": "Gebruikers van dit pad weergeven",
|
||||
|
|
|
@ -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.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.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.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",
|
||||
|
@ -78,6 +78,7 @@
|
|||
"pad.share.emebdcode": "URL \u0431\u0430\u0432\u04d5\u0440\u044b\u043d",
|
||||
"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.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.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:",
|
||||
|
@ -99,6 +100,8 @@
|
|||
"timeslider.month.october": "\u043e\u043a\u0442\u044f\u0431\u0440\u044c",
|
||||
"timeslider.month.november": "\u043d\u043e\u044f\u0431\u0440\u044c",
|
||||
"timeslider.month.december": "\u0434\u0435\u043a\u0430\u0431\u0440\u044c",
|
||||
"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.userlist.entername": "\u0414\u04d5 \u043d\u043e\u043c \u0431\u0430\u0444\u044b\u0441\u0441",
|
||||
"pad.userlist.unnamed": "\u04d5\u043d\u04d5\u043d\u043e\u043c",
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
"pad.toolbar.clearAuthorship.title": "Usu\u0144 kolory autor\u00f3w",
|
||||
"pad.toolbar.import_export.title": "Import\/eksport z\/do r\u00f3\u017cnych format\u00f3w plik\u00f3w",
|
||||
"pad.toolbar.timeslider.title": "O\u015b czasu",
|
||||
"pad.toolbar.savedRevision.title": "Zapisane wersje",
|
||||
"pad.toolbar.savedRevision.title": "Zapisz wersj\u0119",
|
||||
"pad.toolbar.settings.title": "Ustawienia",
|
||||
"pad.toolbar.embed.title": "Umie\u015b\u0107 ten Notatnik",
|
||||
"pad.toolbar.showusers.title": "Poka\u017c u\u017cytkownik\u00f3w",
|
||||
|
@ -102,6 +102,8 @@
|
|||
"timeslider.month.october": "Pa\u017adziernik",
|
||||
"timeslider.month.november": "Listopad",
|
||||
"timeslider.month.december": "Grudzie\u0144",
|
||||
"timeslider.unnamedauthor": "{{num}} nienazwany autor",
|
||||
"timeslider.unnamedauthors": "{{num}} autor\u00f3w bez nazw",
|
||||
"pad.savedrevs.marked": "Ta wersja zosta\u0142a w\u0142a\u015bnie oznaczona jako zapisana.",
|
||||
"pad.userlist.entername": "Wprowad\u017a swoj\u0105 nazw\u0119",
|
||||
"pad.userlist.unnamed": "bez nazwy",
|
||||
|
|
|
@ -17,7 +17,9 @@
|
|||
"pad.settings.fontType": "\u0644\u064a\u06a9\u0628\u06bc\u06d0 \u0689\u0648\u0644:",
|
||||
"pad.settings.fontType.normal": "\u0646\u0648\u0631\u0645\u0627\u0644",
|
||||
"pad.settings.fontType.monospaced": "\u0645\u0648\u0646\u0648\u0633\u067e\u06d0\u0633",
|
||||
"pad.settings.globalView": "\u0646\u0693\u06d0\u0648\u0627\u0644\u0647 \u069a\u06a9\u0627\u0631\u06d0\u062f\u0646\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.exportplain": "\u0633\u0627\u062f\u0647 \u0645\u062a\u0646",
|
||||
"pad.importExport.exportword": "\u0645\u0627\u064a\u06a9\u0631\u0648\u0633\u0627\u0641\u067c \u0648\u0631\u0689",
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
"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.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.embed.title": "Incorporar esta Nota",
|
||||
"pad.toolbar.showusers.title": "Mostrar os usuarios nesta Nota",
|
||||
|
@ -100,6 +100,8 @@
|
|||
"timeslider.month.october": "Outubro",
|
||||
"timeslider.month.november": "Novembro",
|
||||
"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.userlist.entername": "Insira o seu nome",
|
||||
"pad.userlist.unnamed": "Sem t\u00edtulo",
|
||||
|
|
|
@ -103,6 +103,8 @@
|
|||
"timeslider.month.october": "\u043e\u043a\u0442\u044f\u0431\u0440\u044c",
|
||||
"timeslider.month.november": "\u043d\u043e\u044f\u0431\u0440\u044c",
|
||||
"timeslider.month.december": "\u0434\u0435\u043a\u0430\u0431\u0440\u044c",
|
||||
"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.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",
|
||||
|
|
|
@ -100,6 +100,8 @@
|
|||
"timeslider.month.october": "Oktober",
|
||||
"timeslider.month.november": "November",
|
||||
"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.userlist.entername": "Vpi\u0161ite ime",
|
||||
"pad.userlist.unnamed": "neimenovana oseba",
|
||||
|
|
|
@ -100,6 +100,8 @@
|
|||
"timeslider.month.october": "oktober",
|
||||
"timeslider.month.november": "november",
|
||||
"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.userlist.entername": "Ange ditt namn",
|
||||
"pad.userlist.unnamed": "namnl\u00f6s",
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
"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": "\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.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",
|
||||
|
|
|
@ -102,6 +102,8 @@
|
|||
"timeslider.month.october": "\u0416\u043e\u0432\u0442\u0435\u043d\u044c",
|
||||
"timeslider.month.november": "\u041b\u0438\u0441\u0442\u043e\u043f\u0430\u0434",
|
||||
"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.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",
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
"authors": [
|
||||
"Dimension",
|
||||
"Hydra",
|
||||
"Yfdyh000",
|
||||
"\u4e4c\u62c9\u8de8\u6c2a",
|
||||
"\u71c3\u7389"
|
||||
]
|
||||
},
|
||||
|
@ -18,8 +20,9 @@
|
|||
"pad.toolbar.undo.title": "\u64a4\u6d88 (Ctrl-Z)",
|
||||
"pad.toolbar.redo.title": "\u91cd\u505a (Ctrl-Y)",
|
||||
"pad.toolbar.clearAuthorship.title": "\u6e05\u9664\u4f5c\u540d\u989c\u8272",
|
||||
"pad.toolbar.import_export.title": "\u4ee5\u5176\u4ed6\u6587\u4ef6\u683c\u5f0f\u5bfc\u5165\/\u5bfc\u51fa",
|
||||
"pad.toolbar.timeslider.title": "\u65f6\u95f4\u8f74",
|
||||
"pad.toolbar.savedRevision.title": "\u5df2\u4fdd\u5b58\u7684\u4fee\u8ba2",
|
||||
"pad.toolbar.savedRevision.title": "\u4fdd\u5b58\u4fee\u8ba2",
|
||||
"pad.toolbar.settings.title": "\u8bbe\u7f6e",
|
||||
"pad.toolbar.embed.title": "\u5d4c\u5165\u6b64\u8bb0\u4e8b\u672c",
|
||||
"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.wrongPassword": "\u60a8\u7684\u5bc6\u7801\u9519\u4e86",
|
||||
"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.fontType": "\u5b57\u4f53\u7c7b\u578b\uff1a",
|
||||
"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.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.export": "\u5bfc\u51fa\u76ee\u524d\u7684\u8bb0\u4e8b\u7c3f\u4e3a\uff1a",
|
||||
"pad.importExport.exporthtml": "HTML",
|
||||
"pad.importExport.exportplain": "\u7eaf\u6587\u672c",
|
||||
"pad.importExport.exportword": "Microsoft Word",
|
||||
|
@ -43,21 +53,36 @@
|
|||
"pad.importExport.exportopen": "ODF\uff08\u5f00\u653e\u6587\u6863\u683c\u5f0f\uff09",
|
||||
"pad.importExport.exportdokuwiki": "DokuWiki",
|
||||
"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.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.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.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.explanation": "\u6b64\u8bb0\u4e8b\u672c\u5df2\u88ab\u79fb\u9664\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.readonly": "\u53ea\u80fd\u8bfb",
|
||||
"pad.share.link": "\u94fe\u63a5",
|
||||
"pad.share.emebdcode": "\u5d4c\u5165\u7f51\u5740",
|
||||
"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.authors": "\u4f5c\u8005\uff1a",
|
||||
"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.saved": "\u5728{{year}}\u5e74{{month}}{{day}}\u65e5\u4fdd\u5b58",
|
||||
"timeslider.month.january": "\u4e00\u6708",
|
||||
|
@ -72,13 +97,20 @@
|
|||
"timeslider.month.october": "\u5341\u6708",
|
||||
"timeslider.month.november": "\u5341\u4e00\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.unnamed": "\u65e0\u540d",
|
||||
"pad.userlist.guest": "\u8bbf\u5ba2",
|
||||
"pad.userlist.deny": "\u62d2\u7edd",
|
||||
"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.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.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"
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": {
|
||||
"1": "Simon Shek"
|
||||
"0": "Shirayuki",
|
||||
"2": "Simon Shek"
|
||||
}
|
||||
},
|
||||
"index.newPad": "\u65b0Pad",
|
||||
|
@ -19,7 +20,7 @@
|
|||
"pad.toolbar.clearAuthorship.title": "\u6e05\u9664\u4f5c\u540d\u984f\u8272",
|
||||
"pad.toolbar.import_export.title": "\u4ee5\u5176\u4ed6\u6a94\u6848\u683c\u5f0f\u5c0e\u5165\uff0f\u532f\u51fa",
|
||||
"pad.toolbar.timeslider.title": "\u6642\u9593\u8ef8",
|
||||
"pad.toolbar.savedRevision.title": "\u5df2\u5132\u5b58\u7684\u4fee\u8a02",
|
||||
"pad.toolbar.savedRevision.title": "\u5132\u5b58\u4fee\u8a02",
|
||||
"pad.toolbar.settings.title": "\u8a2d\u5b9a",
|
||||
"pad.toolbar.embed.title": "\u5d4c\u5165\u6b64pad",
|
||||
"pad.toolbar.showusers.title": "\u986f\u793a\u6b64pad\u7684\u7528\u6236",
|
||||
|
@ -86,8 +87,8 @@
|
|||
"timeslider.toolbar.exportlink.title": "\u532f\u51fa",
|
||||
"timeslider.exportCurrent": "\u532f\u51fa\u7576\u524d\u7248\u672c\u70ba\uff1a",
|
||||
"timeslider.version": "\u7248\u672c{{version}}",
|
||||
"timeslider.saved": "{{year}}{{month}}{{day}}\u4fdd\u5b58",
|
||||
"timeslider.dateformat": "{{year}}{{month}}{{day}} {{hours}}:{{minutes}}:{{seconds}}",
|
||||
"timeslider.saved": "{{year}}\u5e74{{month}}{{day}}\u65e5\u4fdd\u5b58",
|
||||
"timeslider.dateformat": "{{year}}\u5e74{{month}}\u6708{{day}}\u65e5 {{hours}}:{{minutes}}:{{seconds}}",
|
||||
"timeslider.month.january": "1\u6708",
|
||||
"timeslider.month.february": "2\u6708",
|
||||
"timeslider.month.march": "3\u6708",
|
||||
|
@ -100,6 +101,8 @@
|
|||
"timeslider.month.october": "10\u6708",
|
||||
"timeslider.month.november": "11\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.userlist.entername": "\u8f38\u5165\u60a8\u7684\u59d3\u540d",
|
||||
"pad.userlist.unnamed": "\u672a\u547d\u540d",
|
||||
|
|
|
@ -253,9 +253,7 @@ exports.getHTML = function(padID, rev, callback)
|
|||
exportHtml.getPadHTML(pad, undefined, function (err, html)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
|
||||
data = {html: html};
|
||||
|
||||
callback(null, data);
|
||||
});
|
||||
}
|
||||
|
@ -325,17 +323,17 @@ exports.getChatHistory = function(padID, start, end, callback)
|
|||
if(!start || !end)
|
||||
{
|
||||
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"));
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -146,12 +146,11 @@ exports.getPad = function(id, text, callback)
|
|||
else
|
||||
{
|
||||
pad = new Pad(id);
|
||||
|
||||
|
||||
//initalize the pad
|
||||
pad.init(text, function(err)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
|
||||
globalPads.set(id, pad);
|
||||
callback(null, pad);
|
||||
});
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
||||
/*
|
||||
|
|
82
src/node/db/SessionStore.js
Normal file
82
src/node/db/SessionStore.js
Normal 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);
|
||||
};
|
|
@ -216,6 +216,9 @@ var version =
|
|||
}
|
||||
};
|
||||
|
||||
// set the latest available API version here
|
||||
exports.latestApiVersion = '1.2.7';
|
||||
|
||||
/**
|
||||
* Handles a HTTP API call
|
||||
* @param functionName the name of the called function
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
var ERR = require("async-stacktrace");
|
||||
var exporthtml = require("../utils/ExportHtml");
|
||||
var exporttxt = require("../utils/ExportTxt");
|
||||
var exportdokuwiki = require("../utils/ExportDokuWiki");
|
||||
var padManager = require("../db/PadManager");
|
||||
var async = require("async");
|
||||
|
@ -48,22 +49,75 @@ exports.doExport = function(req, res, padId, type)
|
|||
res.attachment(padId + "." + type);
|
||||
|
||||
//if this is a plain text export, we can do this directly
|
||||
// We have to over engineer this because tabs are stored as attributes and not plain text
|
||||
|
||||
if(type == "txt")
|
||||
{
|
||||
padManager.getPad(padId, function(err, pad)
|
||||
{
|
||||
ERR(err);
|
||||
if(req.params.rev){
|
||||
pad.getInternalRevisionAText(req.params.rev, function(junk, text)
|
||||
{
|
||||
res.send(text.text ? text.text : null);
|
||||
});
|
||||
}
|
||||
else
|
||||
var txt;
|
||||
var randNum;
|
||||
var srcFile, destFile;
|
||||
|
||||
async.series([
|
||||
//render the txt document
|
||||
function(callback)
|
||||
{
|
||||
res.send(pad.text());
|
||||
exporttxt.getPadTXTDocument(padId, req.params.rev, false, function(err, _txt)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
txt = _txt;
|
||||
callback();
|
||||
});
|
||||
},
|
||||
//decide what to do with the txt export
|
||||
function(callback)
|
||||
{
|
||||
//if this is a txt export, we can send this from here directly
|
||||
res.send(txt);
|
||||
callback("stop");
|
||||
},
|
||||
//send the convert job to abiword
|
||||
function(callback)
|
||||
{
|
||||
//ensure html can be collected by the garbage collector
|
||||
txt = null;
|
||||
|
||||
destFile = tempDirectory + "/eplite_export_" + randNum + "." + type;
|
||||
abiword.convertFile(srcFile, destFile, type, callback);
|
||||
},
|
||||
//send the file
|
||||
function(callback)
|
||||
{
|
||||
res.sendfile(destFile, null, callback);
|
||||
},
|
||||
//clean up temporary files
|
||||
function(callback)
|
||||
{
|
||||
async.parallel([
|
||||
function(callback)
|
||||
{
|
||||
fs.unlink(srcFile, callback);
|
||||
},
|
||||
function(callback)
|
||||
{
|
||||
//100ms delay to accomidate for slow windows fs
|
||||
if(os.type().indexOf("Windows") > -1)
|
||||
{
|
||||
setTimeout(function()
|
||||
{
|
||||
fs.unlink(destFile, callback);
|
||||
}, 100);
|
||||
}
|
||||
else
|
||||
{
|
||||
fs.unlink(destFile, callback);
|
||||
}
|
||||
}
|
||||
], callback);
|
||||
}
|
||||
});
|
||||
], function(err)
|
||||
{
|
||||
if(err && err != "stop") ERR(err);
|
||||
})
|
||||
}
|
||||
else if(type == 'dokuwiki')
|
||||
{
|
||||
|
|
|
@ -32,14 +32,10 @@ var securityManager = require("../db/SecurityManager");
|
|||
var plugins = require("ep_etherpad-lite/static/js/pluginfw/plugins.js");
|
||||
var log4js = require('log4js');
|
||||
var messageLogger = log4js.getLogger("message");
|
||||
var accessLogger = log4js.getLogger("access");
|
||||
var _ = require('underscore');
|
||||
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
|
||||
* key = sessionId
|
||||
|
@ -83,14 +79,11 @@ exports.handleConnect = function(client)
|
|||
exports.kickSessionsFromPad = function(padID)
|
||||
{
|
||||
//skip if there is nobody on this pad
|
||||
if(!pad2sessions[padID])
|
||||
if(socketio.sockets.clients(padID).length == 0)
|
||||
return;
|
||||
|
||||
//disconnect everyone from this pad
|
||||
for(var i in pad2sessions[padID])
|
||||
{
|
||||
socketio.sockets.sockets[pad2sessions[padID][i]].json.send({disconnect:"deleted"});
|
||||
}
|
||||
socketio.sockets.in(padID).json.send({disconnect:"deleted"});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -100,15 +93,13 @@ exports.kickSessionsFromPad = function(padID)
|
|||
exports.handleDisconnect = function(client)
|
||||
{
|
||||
//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(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
|
||||
authorManager.getAuthorColorId(author, function(err, color)
|
||||
authorManager.getAuthorColorId(session.author, function(err, color)
|
||||
{
|
||||
ERR(err);
|
||||
|
||||
|
@ -121,32 +112,19 @@ exports.handleDisconnect = function(client)
|
|||
"ip": "127.0.0.1",
|
||||
"colorId": color,
|
||||
"userAgent": "Anonymous",
|
||||
"userId": author
|
||||
"userId": session.author
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//Go trough all user that are still on the pad, and send them the USER_LEAVE message
|
||||
for(i in pad2sessions[sessionPad])
|
||||
{
|
||||
var socket = socketio.sockets.sockets[pad2sessions[sessionPad][i]];
|
||||
if(socket !== undefined){
|
||||
socket.json.send(messageToTheOtherUsers);
|
||||
}
|
||||
|
||||
}
|
||||
client.broadcast.to(session.padId).json.send(messageToTheOtherUsers);
|
||||
});
|
||||
}
|
||||
|
||||
//Go trough all sessions of this pad, search and destroy the entry of this client
|
||||
for(i in pad2sessions[sessionPad])
|
||||
{
|
||||
if(pad2sessions[sessionPad][i] == client.id)
|
||||
{
|
||||
pad2sessions[sessionPad].splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
client.get('remoteAddress', function(er, ip) {
|
||||
accessLogger.info('[LEAVE] Pad "'+session.padId+'": Author "'+session.author+'" on client '+client.id+' with IP "'+ip+'" left the pad')
|
||||
})
|
||||
|
||||
//Delete the sessioninfos entrys of this session
|
||||
delete sessioninfos[client.id];
|
||||
|
@ -228,11 +206,10 @@ exports.handleMessage = function(client, message)
|
|||
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
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
// If the message has a padId we assume the client is already known to the server and needs no re-authorization
|
||||
if(!message.padId)
|
||||
return callback();
|
||||
|
||||
// Note: message.sessionID is an entirely different kind of
|
||||
// session from the sessions we use here! Beware! FIXME: Call
|
||||
// our "sessions" "connections".
|
||||
|
@ -292,9 +269,7 @@ exports.handleCustomMessage = function (padID, msg, cb) {
|
|||
time: time
|
||||
}
|
||||
};
|
||||
for (var i in pad2sessions[padID]) {
|
||||
socketio.sockets.sockets[pad2sessions[padID][i]].json.send(msg);
|
||||
}
|
||||
socketio.sockets.in(padID).json.send(msg);
|
||||
|
||||
cb(null, {});
|
||||
}
|
||||
|
@ -352,10 +327,7 @@ function handleChatMessage(client, message)
|
|||
};
|
||||
|
||||
//broadcast the chat message to everyone on the pad
|
||||
for(var i in pad2sessions[padId])
|
||||
{
|
||||
socketio.sockets.sockets[pad2sessions[padId][i]].json.send(msg);
|
||||
}
|
||||
socketio.sockets.in(padId).json.send(msg);
|
||||
|
||||
callback();
|
||||
}
|
||||
|
@ -413,23 +385,16 @@ function handleGetChatMessages(client, message)
|
|||
{
|
||||
if(ERR(err, callback)) return;
|
||||
|
||||
var infoMsg = {
|
||||
type: "COLLABROOM",
|
||||
data: {
|
||||
type: "CHAT_MESSAGES",
|
||||
messages: chatMessages
|
||||
}
|
||||
};
|
||||
|
||||
// send the messages back to the client
|
||||
for(var i in pad2sessions[padId])
|
||||
{
|
||||
if(pad2sessions[padId][i] == client.id)
|
||||
{
|
||||
socketio.sockets.sockets[pad2sessions[padId][i]].json.send(infoMsg);
|
||||
break;
|
||||
var infoMsg = {
|
||||
type: "COLLABROOM",
|
||||
data: {
|
||||
type: "CHAT_MESSAGES",
|
||||
messages: chatMessages
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// send the messages back to the client
|
||||
client.json.send(infoMsg);
|
||||
});
|
||||
}]);
|
||||
}
|
||||
|
@ -453,14 +418,14 @@ function handleSuggestUserName(client, message)
|
|||
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
|
||||
for(var i in pad2sessions[padId])
|
||||
{
|
||||
if(sessioninfos[pad2sessions[padId][i]].author == message.data.payload.unnamedId)
|
||||
{
|
||||
socketio.sockets.sockets[pad2sessions[padId][i]].send(message);
|
||||
for(var i = 0; i < clients.length; i++) {
|
||||
var session = sessioninfos[clients[i].id];
|
||||
if(session && session.author == message.data.payload.unnamedId) {
|
||||
clients[i].json.send(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -501,7 +466,8 @@ function handleUserInfoUpdate(client, message)
|
|||
type: "USER_NEWINFO",
|
||||
userInfo: {
|
||||
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,
|
||||
userAgent: "Anonymous",
|
||||
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
|
||||
for(var i in pad2sessions[padId])
|
||||
{
|
||||
if(pad2sessions[padId][i] != client.id)
|
||||
{
|
||||
socketio.sockets.sockets[pad2sessions[padId][i]].json.send(infoMsg);
|
||||
}
|
||||
}
|
||||
client.broadcast.to(padId).json.send(infoMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -632,7 +586,14 @@ function handleUserChanges(client, message)
|
|||
// client) are relative to revision r - 1. The follow function
|
||||
// rebases "changeset" so that it is relative to revision r
|
||||
// and can be applied after "c".
|
||||
changeset = Changeset.follow(c, changeset, false, apool);
|
||||
try
|
||||
{
|
||||
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
|
||||
async.nextTick(callback);
|
||||
|
@ -682,90 +643,76 @@ function handleUserChanges(client, message)
|
|||
exports.updatePadClients = function(pad, callback)
|
||||
{
|
||||
//skip this step if noone is on this pad
|
||||
if(!pad2sessions[pad.id])
|
||||
{
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
var roomClients = socketio.sockets.clients(pad.id);
|
||||
if(roomClients.length==0)
|
||||
return callback();
|
||||
|
||||
// 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
|
||||
async.forEach(pad2sessions[pad.id], function(session, callback)
|
||||
async.forEach(roomClients, function(client, callback)
|
||||
{
|
||||
var sid = client.id;
|
||||
|
||||
//https://github.com/caolan/async#whilst
|
||||
//send them all new changesets
|
||||
async.whilst(
|
||||
function (){ return sessioninfos[session] && sessioninfos[session].rev < pad.getHeadRevisionNumber()},
|
||||
function (){ return sessioninfos[sid] && sessioninfos[sid].rev < pad.getHeadRevisionNumber()},
|
||||
function(callback)
|
||||
{
|
||||
var author, revChangeset, currentTime;
|
||||
var r = sessioninfos[session].rev + 1;
|
||||
|
||||
async.parallel([
|
||||
function (callback)
|
||||
{
|
||||
pad.getRevisionAuthor(r, function(err, value)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
author = value;
|
||||
callback();
|
||||
});
|
||||
},
|
||||
function (callback)
|
||||
{
|
||||
pad.getRevisionChangeset(r, function(err, value)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
revChangeset = value;
|
||||
callback();
|
||||
});
|
||||
},
|
||||
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
|
||||
if(sessioninfos[session] == null)
|
||||
{
|
||||
callback(null);
|
||||
return;
|
||||
}
|
||||
if(author == sessioninfos[session].author)
|
||||
{
|
||||
socketio.sockets.sockets[session].json.send({"type":"COLLABROOM","data":{type:"ACCEPT_COMMIT", newRev:r}});
|
||||
}
|
||||
else
|
||||
{
|
||||
var forWire = Changeset.prepareForWire(revChangeset, pad.pool);
|
||||
var wireMsg = {"type":"COLLABROOM",
|
||||
"data":{type:"NEW_CHANGES",
|
||||
newRev:r,
|
||||
changeset: forWire.translated,
|
||||
apool: forWire.pool,
|
||||
author: author,
|
||||
currentTime: currentTime,
|
||||
timeDelta: currentTime - sessioninfos[session].time
|
||||
}};
|
||||
|
||||
socketio.sockets.sockets[session].json.send(wireMsg);
|
||||
}
|
||||
var r = sessioninfos[sid].rev + 1;
|
||||
|
||||
if(sessioninfos[session] != null)
|
||||
{
|
||||
sessioninfos[session].time = currentTime;
|
||||
sessioninfos[session].rev = r;
|
||||
}
|
||||
|
||||
callback(null);
|
||||
});
|
||||
async.waterfall([
|
||||
function(callback) {
|
||||
if(revCache[r])
|
||||
callback(null, revCache[r]);
|
||||
else
|
||||
pad.getRevision(r, callback);
|
||||
},
|
||||
function(revision, callback)
|
||||
{
|
||||
revCache[r] = revision;
|
||||
|
||||
var author = revision.meta.author,
|
||||
revChangeset = revision.changeset,
|
||||
currentTime = revision.meta.timestamp;
|
||||
|
||||
// next if session has not been deleted
|
||||
if(sessioninfos[sid] == null)
|
||||
return callback(null);
|
||||
|
||||
if(author == sessioninfos[sid].author)
|
||||
{
|
||||
client.json.send({"type":"COLLABROOM","data":{type:"ACCEPT_COMMIT", newRev:r}});
|
||||
}
|
||||
else
|
||||
{
|
||||
var forWire = Changeset.prepareForWire(revChangeset, pad.pool);
|
||||
var wireMsg = {"type":"COLLABROOM",
|
||||
"data":{type:"NEW_CHANGES",
|
||||
newRev:r,
|
||||
changeset: forWire.translated,
|
||||
apool: forWire.pool,
|
||||
author: author,
|
||||
currentTime: currentTime,
|
||||
timeDelta: currentTime - sessioninfos[sid].time
|
||||
}};
|
||||
|
||||
client.json.send(wireMsg);
|
||||
}
|
||||
|
||||
sessioninfos[sid].time = currentTime;
|
||||
sessioninfos[sid].rev = r;
|
||||
|
||||
callback(null);
|
||||
}
|
||||
], callback);
|
||||
},
|
||||
callback
|
||||
);
|
||||
|
@ -895,23 +842,14 @@ function handleClientReady(client, message)
|
|||
function(callback)
|
||||
{
|
||||
async.parallel([
|
||||
//get colorId
|
||||
//get colorId and name
|
||||
function(callback)
|
||||
{
|
||||
authorManager.getAuthorColorId(author, function(err, value)
|
||||
authorManager.getAuthor(author, function(err, value)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
authorColorId = value;
|
||||
callback();
|
||||
});
|
||||
},
|
||||
//get author name
|
||||
function(callback)
|
||||
{
|
||||
authorManager.getAuthorName(author, function(err, value)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
authorName = value;
|
||||
authorColorId = value.colorId;
|
||||
authorName = value.name;
|
||||
callback();
|
||||
});
|
||||
},
|
||||
|
@ -965,21 +903,17 @@ function handleClientReady(client, message)
|
|||
{
|
||||
//Check that the client is still here. It might have disconnected between callbacks.
|
||||
if(sessioninfos[client.id] === undefined)
|
||||
{
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
return callback();
|
||||
|
||||
//Check if this author is already on the pad, if yes, kick the other sessions!
|
||||
if(pad2sessions[padIds.padId])
|
||||
{
|
||||
for(var i in pad2sessions[padIds.padId])
|
||||
{
|
||||
if(sessioninfos[pad2sessions[padIds.padId][i]] && sessioninfos[pad2sessions[padIds.padId][i]].author == author)
|
||||
{
|
||||
var socket = socketio.sockets.sockets[pad2sessions[padIds.padId][i]];
|
||||
if(socket) socket.json.send({disconnect:"userdup"});
|
||||
}
|
||||
var roomClients = socketio.sockets.clients(padIds.padId);
|
||||
for(var i = 0; i < roomClients.length; i++) {
|
||||
var sinfo = sessioninfos[roomClients[i].id];
|
||||
if(sinfo && sinfo.author == author) {
|
||||
// fix user's counter, works on page refresh or if user closes browser window and then rejoins
|
||||
sessioninfos[roomClients[i].id] = {};
|
||||
roomClients[i].leave(padIds.padId);
|
||||
roomClients[i].json.send({disconnect:"userdup"});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -988,18 +922,21 @@ function handleClientReady(client, message)
|
|||
sessioninfos[client.id].readOnlyPadId = padIds.readOnlyPadId;
|
||||
sessioninfos[client.id].readonly = padIds.readonly;
|
||||
|
||||
//check if there is already a pad2sessions entry, if not, create one
|
||||
if(!pad2sessions[padIds.padId])
|
||||
{
|
||||
pad2sessions[padIds.padId] = [];
|
||||
}
|
||||
|
||||
//Saves in pad2sessions that this session belongs to this pad
|
||||
pad2sessions[padIds.padId].push(client.id);
|
||||
|
||||
//Log creation/(re-)entering of a pad
|
||||
client.get('remoteAddress', function(er, ip) {
|
||||
if(pad.head > 0) {
|
||||
accessLogger.info('[ENTER] Pad "'+padIds.padId+'": Client '+client.id+' with IP "'+ip+'" entered the pad');
|
||||
}
|
||||
else if(pad.head == 0) {
|
||||
accessLogger.info('[CREATE] Pad "'+padIds.padId+'": Client '+client.id+' with IP "'+ip+'" created the pad');
|
||||
}
|
||||
})
|
||||
|
||||
//If this is a reconnect, we don't have to send the client the ClientVars again
|
||||
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
|
||||
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
|
||||
// used to request the latest 100 chat-messages later (GET_CHAT_MESSAGES)
|
||||
"chatHead": pad.chatHead,
|
||||
"numConnectedUsers": pad2sessions[padIds.padId].length,
|
||||
"isProPad": false,
|
||||
"numConnectedUsers": roomClients.length,
|
||||
"readOnlyId": padIds.readOnlyPadId,
|
||||
"readonly": padIds.readonly,
|
||||
"serverTimestamp": new Date().getTime(),
|
||||
"globalPadId": message.padId,
|
||||
"userId": author,
|
||||
"cookiePrefsToSet": {
|
||||
"fullWidth": false,
|
||||
"hideSidebar": false
|
||||
},
|
||||
"abiwordAvailable": settings.abiwordAvailable(),
|
||||
"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
|
||||
client.json.send({type: "CLIENT_VARS", data: clientVars});
|
||||
//Save the current revision in sessioninfos, should be the same as in clientVars
|
||||
|
@ -1108,74 +1042,56 @@ function handleClientReady(client, message)
|
|||
{
|
||||
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
|
||||
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
|
||||
//sessionID is still assigned to a valid session
|
||||
if(sessioninfos[sessionID] !== undefined &&
|
||||
socketio.sockets.sockets[sessionID] !== undefined){
|
||||
author = sessioninfos[sessionID].author;
|
||||
socket = socketio.sockets.sockets[sessionID];
|
||||
}else {
|
||||
// If the sessionID is not valid, callback();
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
async.series([
|
||||
if(sessioninfos[roomClient.id] !== undefined)
|
||||
author = sessioninfos[roomClient.id].author;
|
||||
else // If the client id is not valid, callback();
|
||||
return callback();
|
||||
|
||||
async.waterfall([
|
||||
//get the authorname & colorId
|
||||
function(callback)
|
||||
{
|
||||
async.parallel([
|
||||
function(callback)
|
||||
{
|
||||
authorManager.getAuthorColorId(author, function(err, value)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
sessionAuthorColorId = value;
|
||||
callback();
|
||||
})
|
||||
},
|
||||
function(callback)
|
||||
{
|
||||
authorManager.getAuthorName(author, function(err, value)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
sessionAuthorName = value;
|
||||
callback();
|
||||
})
|
||||
}
|
||||
],callback);
|
||||
// reuse previously created cache of author's data
|
||||
if(historicalAuthorData[author])
|
||||
callback(null, historicalAuthorData[author]);
|
||||
else
|
||||
authorManager.getAuthor(author, callback);
|
||||
},
|
||||
function (callback)
|
||||
function (authorInfo, 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
|
||||
var messageToNotifyTheClientAboutTheOthers = {
|
||||
"type": "COLLABROOM",
|
||||
"data": {
|
||||
type: "USER_NEWINFO",
|
||||
userInfo: {
|
||||
"ip": "127.0.0.1",
|
||||
"colorId": sessionAuthorColorId,
|
||||
"name": sessionAuthorName,
|
||||
"userAgent": "Anonymous",
|
||||
"userId": author
|
||||
}
|
||||
//Send the new User a Notification about this other user
|
||||
var msg = {
|
||||
"type": "COLLABROOM",
|
||||
"data": {
|
||||
type: "USER_NEWINFO",
|
||||
userInfo: {
|
||||
"ip": "127.0.0.1",
|
||||
"colorId": authorInfo.colorId,
|
||||
"name": authorInfo.name,
|
||||
"userAgent": "Anonymous",
|
||||
"userId": author
|
||||
}
|
||||
};
|
||||
client.json.send(messageToNotifyTheClientAboutTheOthers);
|
||||
}
|
||||
}
|
||||
};
|
||||
client.json.send(msg);
|
||||
}
|
||||
], callback);
|
||||
], callback);
|
||||
}, callback);
|
||||
}
|
||||
],function(err)
|
||||
|
@ -1521,33 +1437,30 @@ function composePadChangesets(padId, startNum, endNum, callback)
|
|||
* Get the number of users in a pad
|
||||
*/
|
||||
exports.padUsersCount = function (padID, callback) {
|
||||
if (!pad2sessions[padID] || typeof pad2sessions[padID] != typeof []) {
|
||||
callback(null, {padUsersCount: 0});
|
||||
} else {
|
||||
callback(null, {padUsersCount: pad2sessions[padID].length});
|
||||
}
|
||||
callback(null, {
|
||||
padUsersCount: socketio.sockets.clients(padID).length
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of users in a pad
|
||||
*/
|
||||
exports.padUsers = function (padID, callback) {
|
||||
if (!pad2sessions[padID] || typeof pad2sessions[padID] != typeof []) {
|
||||
callback(null, {padUsers: []});
|
||||
} else {
|
||||
var authors = [];
|
||||
for ( var ix in sessioninfos ) {
|
||||
if ( sessioninfos[ix].padId !== padID ) {
|
||||
continue;
|
||||
}
|
||||
var aid = sessioninfos[ix].author;
|
||||
authorManager.getAuthor( aid, function ( err, author ) {
|
||||
author.id = aid;
|
||||
authors.push( author );
|
||||
if ( authors.length === pad2sessions[padID].length ) {
|
||||
callback(null, {padUsers: authors});
|
||||
}
|
||||
} );
|
||||
var result = [];
|
||||
|
||||
async.forEach(socketio.sockets.clients(padId), function(roomClient, callback) {
|
||||
var s = sessioninfos[roomClient.id];
|
||||
if(s) {
|
||||
authorManager.getAuthor(s.author, function(err, author) {
|
||||
if(ERR(err, callback)) return;
|
||||
|
||||
author.id = s.author;
|
||||
result.push(author);
|
||||
});
|
||||
}
|
||||
}
|
||||
}, function(err) {
|
||||
if(ERR(err, callback)) return;
|
||||
|
||||
callback(null, {padUsers: result});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -55,13 +55,14 @@ exports.setSocketIO = function(_socket)
|
|||
|
||||
socket.sockets.on('connection', function(client)
|
||||
{
|
||||
client.set('remoteAddress', client.handshake.address.address);
|
||||
var clientAuthorized = false;
|
||||
|
||||
//wrap the original send function to log the messages
|
||||
client._send = client.send;
|
||||
client.send = function(message)
|
||||
{
|
||||
messageLogger.info("to " + client.id + ": " + stringifyWithoutPassword(message));
|
||||
messageLogger.debug("to " + client.id + ": " + stringifyWithoutPassword(message));
|
||||
client._send(message);
|
||||
}
|
||||
|
||||
|
@ -79,7 +80,7 @@ exports.setSocketIO = function(_socket)
|
|||
//check if component is registered in the components array
|
||||
if(components[message.component])
|
||||
{
|
||||
messageLogger.info("from " + client.id + ": " + stringifyWithoutPassword(message));
|
||||
messageLogger.debug("from " + client.id + ": " + stringifyWithoutPassword(message));
|
||||
components[message.component].handleMessage(client, message);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,4 +57,9 @@ exports.expressCreateServer = function (hook_name, args, cb) {
|
|||
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});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,14 +1,26 @@
|
|||
var path = require("path")
|
||||
, npm = require("npm")
|
||||
, fs = require("fs");
|
||||
, fs = require("fs")
|
||||
, async = require("async");
|
||||
|
||||
exports.expressCreateServer = function (hook_name, args, cb) {
|
||||
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); }
|
||||
|
||||
res.send("var specs_list = " + JSON.stringify(files.sort()) + ";\n");
|
||||
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");
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
var url2FilePath = function(url){
|
||||
|
@ -44,4 +56,29 @@ exports.expressCreateServer = function (hook_name, args, cb) {
|
|||
args.app.get('/tests/frontend', function (req, res) {
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ var httpLogger = log4js.getLogger("http");
|
|||
var settings = require('../../utils/Settings');
|
||||
var randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString;
|
||||
var hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks');
|
||||
|
||||
var ueberStore = require('../../db/SessionStore');
|
||||
|
||||
//checks for basic http auth
|
||||
exports.basicAuth = function (req, res, next) {
|
||||
|
@ -102,15 +102,14 @@ exports.expressConfigure = function (hook_name, args, cb) {
|
|||
* handling it cleaner :) */
|
||||
|
||||
if (!exports.sessionStore) {
|
||||
exports.sessionStore = new express.session.MemoryStore();
|
||||
exports.secret = randomString(32);
|
||||
exports.sessionStore = new ueberStore();
|
||||
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.use(express.session({store: args.app.sessionStore,
|
||||
key: 'express_sid' }));
|
||||
args.app.use(express.session({secret: exports.secret, store: args.app.sessionStore, key: 'express_sid' }));
|
||||
|
||||
args.app.use(exports.basicAuth);
|
||||
}
|
||||
|
||||
|
|
87
src/node/utils/ExportHelper.js
Normal file
87
src/node/utils/ExportHelper.js
Normal 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) + ";"
|
||||
});
|
||||
}
|
|
@ -21,31 +21,9 @@ var padManager = require("../db/PadManager");
|
|||
var ERR = require("async-stacktrace");
|
||||
var Security = require('ep_etherpad-lite/static/js/security');
|
||||
var hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks');
|
||||
function getPadPlainText(pad, revNum)
|
||||
{
|
||||
var atext = ((revNum !== undefined) ? pad.getInternalRevisionAText(revNum) : pad.atext());
|
||||
var textLines = atext.text.slice(0, -1).split('\n');
|
||||
var attribLines = Changeset.splitAttributionLines(atext.attribs, atext.text);
|
||||
var apool = pad.pool();
|
||||
|
||||
var pieces = [];
|
||||
for (var i = 0; i < textLines.length; i++)
|
||||
{
|
||||
var line = _analyzeLine(textLines[i], attribLines[i], apool);
|
||||
if (line.listLevel)
|
||||
{
|
||||
var numSpaces = line.listLevel * 2 - 1;
|
||||
var bullet = '*';
|
||||
pieces.push(new Array(numSpaces + 1).join(' '), bullet, ' ', line.text, '\n');
|
||||
}
|
||||
else
|
||||
{
|
||||
pieces.push(line.text, '\n');
|
||||
}
|
||||
}
|
||||
|
||||
return pieces.join('');
|
||||
}
|
||||
var getPadPlainText = require('./ExportHelper').getPadPlainText
|
||||
var _analyzeLine = require('./ExportHelper')._analyzeLine;
|
||||
var _encodeWhitespace = require('./ExportHelper')._encodeWhitespace;
|
||||
|
||||
function getPadHTML(pad, revNum, callback)
|
||||
{
|
||||
|
@ -503,45 +481,6 @@ function getHTMLFromAtext(pad, atext, authorColors)
|
|||
return pieces.join('');
|
||||
}
|
||||
|
||||
function _analyzeLine(text, aline, apool)
|
||||
{
|
||||
var line = {};
|
||||
|
||||
// identify list
|
||||
var lineMarker = 0;
|
||||
line.listLevel = 0;
|
||||
if (aline)
|
||||
{
|
||||
var opIter = Changeset.opIterator(aline);
|
||||
if (opIter.hasNext())
|
||||
{
|
||||
var listType = Changeset.opAttributeValue(opIter.next(), 'list', apool);
|
||||
if (listType)
|
||||
{
|
||||
lineMarker = 1;
|
||||
listType = /([a-z]+)([12345678])/.exec(listType);
|
||||
if (listType)
|
||||
{
|
||||
line.listTypeName = listType[1];
|
||||
line.listLevel = Number(listType[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lineMarker)
|
||||
{
|
||||
line.text = text.substring(1);
|
||||
line.aline = Changeset.subattribution(aline, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
line.text = text;
|
||||
line.aline = aline;
|
||||
}
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
exports.getPadHTMLDocument = function (padId, revNum, noDocType, callback)
|
||||
{
|
||||
padManager.getPad(padId, function (err, pad)
|
||||
|
@ -578,79 +517,6 @@ exports.getPadHTMLDocument = function (padId, revNum, noDocType, callback)
|
|||
});
|
||||
}
|
||||
|
||||
function _encodeWhitespace(s) {
|
||||
return s.replace(/[^\x21-\x7E\s\t\n\r]/g, function(c)
|
||||
{
|
||||
return "&#" +c.charCodeAt(0) + ";"
|
||||
});
|
||||
}
|
||||
|
||||
// copied from ACE
|
||||
|
||||
|
||||
function _processSpaces(s)
|
||||
{
|
||||
var doesWrap = true;
|
||||
if (s.indexOf("<") < 0 && !doesWrap)
|
||||
{
|
||||
// short-cut
|
||||
return s.replace(/ /g, ' ');
|
||||
}
|
||||
var parts = [];
|
||||
s.replace(/<[^>]*>?| |[^ <]+/g, function (m)
|
||||
{
|
||||
parts.push(m);
|
||||
});
|
||||
if (doesWrap)
|
||||
{
|
||||
var endOfLine = true;
|
||||
var beforeSpace = false;
|
||||
// last space in a run is normal, others are nbsp,
|
||||
// end of line is nbsp
|
||||
for (var i = parts.length - 1; i >= 0; i--)
|
||||
{
|
||||
var p = parts[i];
|
||||
if (p == " ")
|
||||
{
|
||||
if (endOfLine || beforeSpace) parts[i] = ' ';
|
||||
endOfLine = false;
|
||||
beforeSpace = true;
|
||||
}
|
||||
else if (p.charAt(0) != "<")
|
||||
{
|
||||
endOfLine = false;
|
||||
beforeSpace = false;
|
||||
}
|
||||
}
|
||||
// beginning of line is nbsp
|
||||
for (var i = 0; i < parts.length; i++)
|
||||
{
|
||||
var p = parts[i];
|
||||
if (p == " ")
|
||||
{
|
||||
parts[i] = ' ';
|
||||
break;
|
||||
}
|
||||
else if (p.charAt(0) != "<")
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < parts.length; i++)
|
||||
{
|
||||
var p = parts[i];
|
||||
if (p == " ")
|
||||
{
|
||||
parts[i] = ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
return parts.join('');
|
||||
}
|
||||
|
||||
|
||||
// copied from ACE
|
||||
var _REGEX_WORDCHAR = /[\u0030-\u0039\u0041-\u005A\u0061-\u007A\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\u1FFF\u3040-\u9FFF\uF900-\uFDFF\uFE70-\uFEFE\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFDC]/;
|
||||
|
@ -676,3 +542,57 @@ function _findURLs(text)
|
|||
|
||||
return urls;
|
||||
}
|
||||
|
||||
|
||||
// copied from ACE
|
||||
function _processSpaces(s){
|
||||
var doesWrap = true;
|
||||
if (s.indexOf("<") < 0 && !doesWrap){
|
||||
// short-cut
|
||||
return s.replace(/ /g, ' ');
|
||||
}
|
||||
var parts = [];
|
||||
s.replace(/<[^>]*>?| |[^ <]+/g, function (m){
|
||||
parts.push(m);
|
||||
});
|
||||
if (doesWrap){
|
||||
var endOfLine = true;
|
||||
var beforeSpace = false;
|
||||
// last space in a run is normal, others are nbsp,
|
||||
// end of line is nbsp
|
||||
for (var i = parts.length - 1; i >= 0; i--){
|
||||
var p = parts[i];
|
||||
if (p == " "){
|
||||
if (endOfLine || beforeSpace) parts[i] = ' ';
|
||||
endOfLine = false;
|
||||
beforeSpace = true;
|
||||
}
|
||||
else if (p.charAt(0) != "<"){
|
||||
endOfLine = false;
|
||||
beforeSpace = false;
|
||||
}
|
||||
}
|
||||
// beginning of line is nbsp
|
||||
for (var i = 0; i < parts.length; i++){
|
||||
var p = parts[i];
|
||||
if (p == " "){
|
||||
parts[i] = ' ';
|
||||
break;
|
||||
}
|
||||
else if (p.charAt(0) != "<"){
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < parts.length; i++){
|
||||
var p = parts[i];
|
||||
if (p == " "){
|
||||
parts[i] = ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
return parts.join('');
|
||||
}
|
||||
|
||||
|
|
293
src/node/utils/ExportTxt.js
Normal file
293
src/node/utils/ExportTxt.js
Normal 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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
@ -26,6 +26,8 @@ var argv = require('./Cli').argv;
|
|||
var npm = require("npm/lib/npm.js");
|
||||
var vm = require('vm');
|
||||
var log4js = require("log4js");
|
||||
var randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString;
|
||||
|
||||
|
||||
/* Root path of the installation */
|
||||
exports.root = path.normalize(path.join(npm.dir, ".."));
|
||||
|
@ -112,6 +114,11 @@ exports.loglevel = "INFO";
|
|||
*/
|
||||
exports.logconfig = { appenders: [{ type: "console" }]};
|
||||
|
||||
/*
|
||||
* Session Key, do not sure this.
|
||||
*/
|
||||
exports.sessionKey = false;
|
||||
|
||||
/* This setting is used if you need authentication and/or
|
||||
* authorization. Note: /admin always requires authentication, and
|
||||
* either authorization by a module, or a user with is_admin set */
|
||||
|
@ -132,8 +139,6 @@ exports.abiwordAvailable = function()
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
exports.reloadSettings = function reloadSettings() {
|
||||
// Discover where the settings file lives
|
||||
var settingsFilename = argv.settings || "settings.json";
|
||||
|
@ -152,6 +157,7 @@ exports.reloadSettings = function reloadSettings() {
|
|||
try {
|
||||
if(settingsStr) {
|
||||
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){
|
||||
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.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"){
|
||||
console.warn("DirtyDB is used. This is fine for testing but not recommended for production.")
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
"require-kernel" : "1.0.5",
|
||||
"resolve" : "0.2.x",
|
||||
"socket.io" : "0.9.x",
|
||||
"ueberDB" : "0.1.9",
|
||||
"ueberDB" : "0.1.94",
|
||||
"async" : "0.1.x",
|
||||
"express" : "3.x",
|
||||
"connect" : "2.4.x",
|
||||
|
@ -40,11 +40,10 @@
|
|||
},
|
||||
"bin": { "etherpad-lite": "./node/server.js" },
|
||||
"devDependencies": {
|
||||
"jshint" : "*",
|
||||
"wd" : "0.0.26"
|
||||
"wd" : "0.0.31"
|
||||
},
|
||||
"engines" : { "node" : ">=0.6.0",
|
||||
"engines" : { "node" : ">=0.6.3",
|
||||
"npm" : ">=1.0"
|
||||
},
|
||||
"version" : "1.2.7"
|
||||
"version" : "1.2.8"
|
||||
}
|
||||
|
|
|
@ -1,65 +1,59 @@
|
|||
html, body {
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
color: #333;
|
||||
font: 14px helvetica, sans-serif;
|
||||
background: #ddd;
|
||||
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%;
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
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%;
|
||||
padding: 15px;
|
||||
position: fixed;
|
||||
width: 220px;
|
||||
height: 100%;
|
||||
padding: 15px;
|
||||
width: 220px;
|
||||
border-right: 1px solid #ccc;
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
div.menu ul {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.menu li {
|
||||
list-style: none;
|
||||
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 {
|
||||
display: block;
|
||||
float: right;
|
||||
opacity: 0.9;
|
||||
padding: 15px;
|
||||
max-width: 860px;
|
||||
border-radius: 0 0 7px 7px;
|
||||
margin-left:250px;
|
||||
min-width:400px;
|
||||
padding-left: 265px;
|
||||
}
|
||||
|
||||
#wrapper {
|
||||
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;
|
||||
max-width: 1150px;
|
||||
min-height: 100%;
|
||||
overflow: auto;
|
||||
padding-left: 15px;
|
||||
opacity: .9;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 29px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.separator {
|
||||
margin: 10px 0;
|
||||
height: 1px;
|
||||
|
@ -69,37 +63,45 @@ h2 {
|
|||
background: -ms-linear-gradient(left, #fff, #aaa 20%, #aaa 80%, #fff);
|
||||
background: -o-linear-gradient(left, #fff, #aaa 20%, #aaa 80%, #fff);
|
||||
}
|
||||
|
||||
form {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
#inner {
|
||||
width: 300px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
input {
|
||||
font-weight: bold;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
input[type="button"] {
|
||||
padding: 4px 6px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
table input[type="button"] {
|
||||
float: right;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
border-radius: 3px;
|
||||
box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
padding: 10px;
|
||||
*padding: 0; /* IE7 hack */
|
||||
*padding: 0;
|
||||
/* IE7 hack */
|
||||
width: 100%;
|
||||
outline: none;
|
||||
border: 1px solid #ddd;
|
||||
margin: 0 0 5px 0;
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
table {
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 3px;
|
||||
|
@ -107,34 +109,112 @@ table {
|
|||
width: 100%;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
table thead tr {
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
td, th {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#progress {
|
||||
position: absolute;
|
||||
bottom: 50px;
|
||||
}
|
||||
.settings {
|
||||
margin-top:10px;
|
||||
width:100%;
|
||||
min-height:600px;
|
||||
|
||||
#progress img {
|
||||
vertical-align: top;
|
||||
}
|
||||
#response{
|
||||
display:inline;
|
||||
|
||||
.settings {
|
||||
outline: none;
|
||||
width: 100%;
|
||||
min-height: 500px;
|
||||
}
|
||||
|
||||
#response {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
a:link, a:visited, a:hover, a:focus {
|
||||
color: #333333;
|
||||
text-decoration: none;
|
||||
border-bottom: #333333 1px dotted;
|
||||
}
|
||||
|
||||
a:focus, a:hover {
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -176,3 +176,11 @@ p {
|
|||
}
|
||||
|
||||
#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;
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* This code is mostly from the old Etherpad. Please help us to comment this code.
|
||||
* 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
|
||||
*/
|
||||
|
@ -33,19 +33,6 @@ function object(o)
|
|||
f.prototype = o;
|
||||
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)
|
||||
{
|
||||
|
@ -97,7 +84,6 @@ var noop = function(){};
|
|||
|
||||
exports.isNodeText = isNodeText;
|
||||
exports.object = object;
|
||||
exports.browser = browser;
|
||||
exports.getAssoc = getAssoc;
|
||||
exports.setAssoc = setAssoc;
|
||||
exports.binarySearch = binarySearch;
|
||||
|
|
|
@ -28,7 +28,7 @@ $ = jQuery = require('./rjquery').$;
|
|||
_ = require("./underscore");
|
||||
|
||||
var isNodeText = Ace2Common.isNodeText,
|
||||
browser = Ace2Common.browser,
|
||||
browser = $.browser,
|
||||
getAssoc = Ace2Common.getAssoc,
|
||||
setAssoc = Ace2Common.setAssoc,
|
||||
isTextNode = Ace2Common.isTextNode,
|
||||
|
@ -154,7 +154,8 @@ function Ace2Inner(){
|
|||
var dmesg = noop;
|
||||
window.dmesg = noop;
|
||||
|
||||
var scheduler = parent;
|
||||
|
||||
var scheduler = parent; // hack for opera required
|
||||
|
||||
var textFace = 'monospace';
|
||||
var textSize = 12;
|
||||
|
@ -1621,9 +1622,17 @@ function Ace2Inner(){
|
|||
lines = ccData.lines;
|
||||
var lineAttribs = ccData.lineAttribs;
|
||||
var linesWrapped = ccData.linesWrapped;
|
||||
var scrollToTheLeftNeeded = false;
|
||||
|
||||
if (linesWrapped > 0)
|
||||
{
|
||||
if(!browser.ie){
|
||||
// chrome decides in it's infinite wisdom that its okay to put the browsers visisble window in the middle of the span
|
||||
// an outcome of this is that the first chars of the string are no longer visible to the user.. Yay chrome..
|
||||
// Move the browsers visible area to the left hand side of the span
|
||||
// Firefox isn't quite so bad, but it's still pretty quirky.
|
||||
var scrollToTheLeftNeeded = true;
|
||||
}
|
||||
// console.log("Editor warning: " + linesWrapped + " long line" + (linesWrapped == 1 ? " was" : "s were") + " hard-wrapped into " + ccData.numLinesAfter + " lines.");
|
||||
}
|
||||
|
||||
|
@ -1691,6 +1700,10 @@ function Ace2Inner(){
|
|||
//console.log("removed: "+id);
|
||||
});
|
||||
|
||||
if(scrollToTheLeftNeeded){ // needed to stop chrome from breaking the ui when long strings without spaces are pasted
|
||||
$("#innerdocbody").scrollLeft(0);
|
||||
}
|
||||
|
||||
p.mark("findsel");
|
||||
// if the nodes that define the selection weren't encountered during
|
||||
// content collection, figure out where those nodes are now.
|
||||
|
@ -1896,7 +1909,7 @@ function Ace2Inner(){
|
|||
var prevLine = rep.lines.prev(thisLine);
|
||||
var prevLineText = prevLine.text;
|
||||
var theIndent = /^ *(?:)/.exec(prevLineText)[0];
|
||||
if (/[\[\(\{]\s*$/.exec(prevLineText)) theIndent += THE_TAB;
|
||||
if (/[\[\(\:\{]\s*$/.exec(prevLineText)) theIndent += THE_TAB;
|
||||
var cs = Changeset.builder(rep.lines.totalWidth()).keep(
|
||||
rep.lines.offsetOfIndex(lineNum), lineNum).insert(
|
||||
theIndent, [
|
||||
|
@ -2817,7 +2830,6 @@ function Ace2Inner(){
|
|||
rep.selStart = selectStart;
|
||||
rep.selEnd = selectEnd;
|
||||
rep.selFocusAtStart = newSelFocusAtStart;
|
||||
if (mozillaFakeArrows) mozillaFakeArrows.notifySelectionChanged();
|
||||
currentCallStack.repChanged = true;
|
||||
|
||||
return true;
|
||||
|
@ -3287,7 +3299,7 @@ function Ace2Inner(){
|
|||
listType = /([a-z]+)([12345678])/.exec(listType);
|
||||
var type = listType[1];
|
||||
var level = Number(listType[2]);
|
||||
|
||||
|
||||
//detect empty list item; exclude indentation
|
||||
if(text === '*' && type !== "indent")
|
||||
{
|
||||
|
@ -3317,8 +3329,10 @@ function Ace2Inner(){
|
|||
|
||||
function doIndentOutdent(isOut)
|
||||
{
|
||||
if (!(rep.selStart && rep.selEnd) ||
|
||||
((rep.selStart[0] == rep.selEnd[0]) && (rep.selStart[1] == rep.selEnd[1]) && rep.selEnd[1] > 1))
|
||||
if (!((rep.selStart && rep.selEnd) ||
|
||||
((rep.selStart[0] == rep.selEnd[0]) && (rep.selStart[1] == rep.selEnd[1]) && rep.selEnd[1] > 1)) &&
|
||||
(isOut != true)
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -3326,7 +3340,6 @@ function Ace2Inner(){
|
|||
var firstLine, lastLine;
|
||||
firstLine = rep.selStart[0];
|
||||
lastLine = Math.max(firstLine, rep.selEnd[0] - ((rep.selEnd[1] === 0) ? 1 : 0));
|
||||
|
||||
var mods = [];
|
||||
for (var n = firstLine; n <= lastLine; n++)
|
||||
{
|
||||
|
@ -3539,7 +3552,6 @@ function Ace2Inner(){
|
|||
{
|
||||
// if (DEBUG && window.DONT_INCORP) return;
|
||||
if (!isEditable) return;
|
||||
|
||||
var type = evt.type;
|
||||
var charCode = evt.charCode;
|
||||
var keyCode = evt.keyCode;
|
||||
|
@ -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));
|
||||
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 isTypeForSpecialKey = ((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();
|
||||
specialHandled = true;
|
||||
}
|
||||
if((evt.which == 33 || evt.which == 34) && type == 'keydown'){
|
||||
var oldVisibleLineRange = getVisibleLineRange();
|
||||
var topOffset = rep.selStart[0] - oldVisibleLineRange[0];
|
||||
if(topOffset < 0 ){
|
||||
topOffset = 0;
|
||||
}
|
||||
|
||||
if (mozillaFakeArrows && mozillaFakeArrows.handleKeyEvent(evt))
|
||||
{
|
||||
evt.preventDefault();
|
||||
specialHandled = true;
|
||||
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")
|
||||
|
@ -3801,7 +3879,6 @@ function Ace2Inner(){
|
|||
selection.endPoint = getPointForLineAndChar(se);
|
||||
|
||||
selection.focusAtStart = !! rep.selFocusAtStart;
|
||||
|
||||
setSelection(selection);
|
||||
}
|
||||
|
||||
|
@ -4119,6 +4196,11 @@ function Ace2Inner(){
|
|||
selection.startPoint = pointFromRangeBound(range.startContainer, range.startOffset);
|
||||
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));
|
||||
|
||||
if(selection.startPoint.node.ownerDocument !== window.document){
|
||||
return null;
|
||||
}
|
||||
|
||||
return selection;
|
||||
}
|
||||
else return null;
|
||||
|
@ -5032,331 +5114,6 @@ function Ace2Inner(){
|
|||
}
|
||||
editorInfo.ace_doInsertUnorderedList = doInsertUnorderedList;
|
||||
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 sideDivInner;
|
||||
|
|
|
@ -107,12 +107,16 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded)
|
|||
{
|
||||
newpos = Number(newpos);
|
||||
if (newpos < 0 || newpos > sliderLength) return;
|
||||
if(!newpos){
|
||||
newpos = 0; // stops it from displaying NaN if newpos isn't set
|
||||
}
|
||||
window.location.hash = "#" + newpos;
|
||||
$("#ui-slider-handle").css('left', newpos * ($("#ui-slider-bar").width() - 2) / (sliderLength * 1.0));
|
||||
$("a.tlink").map(function()
|
||||
{
|
||||
$(this).attr('href', $(this).attr('thref').replace("%revision%", newpos));
|
||||
});
|
||||
|
||||
$("#revision_label").html(html10n.get("timeslider.version", { "version": newpos}));
|
||||
|
||||
if (newpos == 0)
|
||||
|
@ -456,31 +460,6 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded)
|
|||
|
||||
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();
|
||||
|
||||
var startPos = clientVars.collab_client_vars.rev;
|
||||
|
|
|
@ -294,8 +294,8 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options, _pad)
|
|||
|
||||
if (newRev != (oldRev + 1))
|
||||
{
|
||||
dmesg("bad message revision on NEW_CHANGES: " + newRev + " not " + (oldRev + 1));
|
||||
setChannelState("DISCONNECTED", "badmessage_newchanges");
|
||||
top.console.warn("bad message revision on NEW_CHANGES: " + newRev + " not " + (oldRev + 1));
|
||||
// setChannelState("DISCONNECTED", "badmessage_newchanges");
|
||||
return;
|
||||
}
|
||||
msgQueue.push(msg);
|
||||
|
@ -304,8 +304,8 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options, _pad)
|
|||
|
||||
if (newRev != (rev + 1))
|
||||
{
|
||||
dmesg("bad message revision on NEW_CHANGES: " + newRev + " not " + (rev + 1));
|
||||
setChannelState("DISCONNECTED", "badmessage_newchanges");
|
||||
top.console.warn("bad message revision on NEW_CHANGES: " + newRev + " not " + (rev + 1));
|
||||
// setChannelState("DISCONNECTED", "badmessage_newchanges");
|
||||
return;
|
||||
}
|
||||
rev = newRev;
|
||||
|
@ -318,8 +318,8 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options, _pad)
|
|||
{
|
||||
if (newRev != (msgQueue[msgQueue.length - 1].newRev + 1))
|
||||
{
|
||||
dmesg("bad message revision on ACCEPT_COMMIT: " + newRev + " not " + (msgQueue[msgQueue.length - 1][0] + 1));
|
||||
setChannelState("DISCONNECTED", "badmessage_acceptcommit");
|
||||
top.console.warn("bad message revision on ACCEPT_COMMIT: " + newRev + " not " + (msgQueue[msgQueue.length - 1][0] + 1));
|
||||
// setChannelState("DISCONNECTED", "badmessage_acceptcommit");
|
||||
return;
|
||||
}
|
||||
msgQueue.push(msg);
|
||||
|
@ -328,8 +328,8 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options, _pad)
|
|||
|
||||
if (newRev != (rev + 1))
|
||||
{
|
||||
dmesg("bad message revision on ACCEPT_COMMIT: " + newRev + " not " + (rev + 1));
|
||||
setChannelState("DISCONNECTED", "badmessage_acceptcommit");
|
||||
top.console.warn("bad message revision on ACCEPT_COMMIT: " + newRev + " not " + (rev + 1));
|
||||
// setChannelState("DISCONNECTED", "badmessage_acceptcommit");
|
||||
return;
|
||||
}
|
||||
rev = newRev;
|
||||
|
|
|
@ -30,8 +30,7 @@ var Security = require('./security');
|
|||
var hooks = require('./pluginfw/hooks');
|
||||
var _ = require('./underscore');
|
||||
var lineAttributeMarker = require('./linestylefilter').lineAttributeMarker;
|
||||
var Ace2Common = require('./ace2_common');
|
||||
var noop = Ace2Common.noop;
|
||||
var noop = function(){};
|
||||
|
||||
|
||||
var domline = {};
|
||||
|
|
|
@ -23,27 +23,27 @@
|
|||
window.html10n = (function(window, document, undefined) {
|
||||
|
||||
// fix console
|
||||
var console = window.console
|
||||
var console = window.console;
|
||||
function interceptConsole(method){
|
||||
if (!console) return function() {}
|
||||
if (!console) return function() {};
|
||||
|
||||
var original = console[method]
|
||||
var original = console[method];
|
||||
|
||||
// do sneaky stuff
|
||||
if (original.bind){
|
||||
// Do this for normal browsers
|
||||
return original.bind(console)
|
||||
return original.bind(console);
|
||||
}else{
|
||||
return function() {
|
||||
// Do this for IE
|
||||
var message = Array.prototype.slice.apply(arguments).join(' ')
|
||||
original(message)
|
||||
var message = Array.prototype.slice.apply(arguments).join(' ');
|
||||
original(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
var consoleLog = interceptConsole('log')
|
||||
, consoleWarn = interceptConsole('warn')
|
||||
, consoleError = interceptConsole('warn')
|
||||
, consoleError = interceptConsole('warn');
|
||||
|
||||
|
||||
// fix Array.prototype.instanceOf in, guess what, IE! <3
|
||||
|
@ -84,14 +84,14 @@ window.html10n = (function(window, document, undefined) {
|
|||
* MicroEvent - to make any js object an event emitter (server or browser)
|
||||
*/
|
||||
|
||||
var MicroEvent = function(){}
|
||||
var MicroEvent = function(){}
|
||||
MicroEvent.prototype = {
|
||||
bind : function(event, fct){
|
||||
bind : function(event, fct){
|
||||
this._events = this._events || {};
|
||||
this._events[event] = this._events[event] || [];
|
||||
this._events[event].push(fct);
|
||||
},
|
||||
unbind : function(event, fct){
|
||||
unbind : function(event, fct){
|
||||
this._events = this._events || {};
|
||||
if( event in this._events === false ) return;
|
||||
this._events[event].splice(this._events[event].indexOf(fct), 1);
|
||||
|
@ -100,7 +100,7 @@ window.html10n = (function(window, document, undefined) {
|
|||
this._events = this._events || {};
|
||||
if( event in this._events === false ) return;
|
||||
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
|
||||
*/
|
||||
function Loader(resources) {
|
||||
this.resources = resources
|
||||
this.cache = {} // file => contents
|
||||
this.langs = {} // lang => strings
|
||||
this.resources = resources;
|
||||
this.cache = {}; // file => contents
|
||||
this.langs = {}; // lang => strings
|
||||
}
|
||||
|
||||
Loader.prototype.load = function(lang, cb) {
|
||||
if(this.langs[lang]) return cb()
|
||||
if(this.langs[lang]) return cb();
|
||||
|
||||
if (this.resources.length > 0) {
|
||||
var reqs = 0;
|
||||
for (var i=0, n=this.resources.length; i < n; i++) {
|
||||
this.fetch(this.resources[i], lang, function(e) {
|
||||
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
|
||||
cb && cb()
|
||||
cb && cb();
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader.prototype.fetch = function(href, lang, cb) {
|
||||
var that = this
|
||||
var that = this;
|
||||
|
||||
if (this.cache[href]) {
|
||||
this.parse(lang, href, this.cache[href], cb)
|
||||
return;
|
||||
}
|
||||
|
||||
var xhr = new XMLHttpRequest()
|
||||
xhr.open('GET', href, /*async: */true)
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', href, /*async: */true);
|
||||
if (xhr.overrideMimeType) {
|
||||
xhr.overrideMimeType('application/json; charset=utf-8');
|
||||
}
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState == 4) {
|
||||
if (xhr.status == 200 || xhr.status === 0) {
|
||||
var data = JSON.parse(xhr.responseText)
|
||||
that.cache[href] = data
|
||||
var data = JSON.parse(xhr.responseText);
|
||||
that.cache[href] = data;
|
||||
// Pass on the contents for parsing
|
||||
that.parse(lang, href, data, cb)
|
||||
that.parse(lang, href, data, cb);
|
||||
} 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) {
|
||||
if ('object' != typeof data) {
|
||||
cb(new Error('A file couldn\'t be parsed as json.'))
|
||||
return
|
||||
cb(new Error('A file couldn\'t be parsed as json.'));
|
||||
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]) {
|
||||
cb(new Error('Couldn\'t find translations for '+lang))
|
||||
return
|
||||
cb(new Error('Couldn\'t find translations for '+lang));
|
||||
return;
|
||||
}
|
||||
|
||||
if ('string' == typeof data[lang]) {
|
||||
// Import rule
|
||||
|
||||
// absolute path
|
||||
var importUrl = data[lang]
|
||||
var importUrl = data[lang];
|
||||
|
||||
// relative path
|
||||
if(data[lang].indexOf("http") != 0 && data[lang].indexOf("/") != 0) {
|
||||
importUrl = currHref+"/../"+data[lang]
|
||||
importUrl = currHref+"/../"+data[lang];
|
||||
}
|
||||
|
||||
this.fetch(importUrl, lang, cb)
|
||||
return
|
||||
this.fetch(importUrl, lang, cb);
|
||||
return;
|
||||
}
|
||||
|
||||
if ('object' != typeof data[lang]) {
|
||||
cb(new Error('Translations should be specified as JSON objects!'))
|
||||
return
|
||||
cb(new Error('Translations should be specified as JSON objects!'));
|
||||
return;
|
||||
}
|
||||
|
||||
this.langs[lang] = data[lang]
|
||||
this.langs[lang] = data[lang];
|
||||
// TODO: Also store accompanying langs
|
||||
cb()
|
||||
cb();
|
||||
}
|
||||
|
||||
|
||||
|
@ -216,11 +216,11 @@ window.html10n = (function(window, document, undefined) {
|
|||
var html10n =
|
||||
{ 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:
|
||||
|
@ -664,14 +664,14 @@ window.html10n = (function(window, document, undefined) {
|
|||
* @param langs An array of lang codes defining fallbacks
|
||||
*/
|
||||
html10n.localize = function(langs) {
|
||||
var that = this
|
||||
var that = this;
|
||||
// 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) {
|
||||
html10n.translations = translations
|
||||
html10n.translateElement(translations)
|
||||
that.trigger('localized')
|
||||
html10n.translations = translations;
|
||||
html10n.translateElement(translations);
|
||||
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
|
||||
*/
|
||||
html10n.translateElement = function(translations, element) {
|
||||
element = element || document.documentElement
|
||||
element = element || document.documentElement;
|
||||
|
||||
var children = element? getTranslatableChildren(element) : document.childNodes;
|
||||
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
|
||||
this.translateNode(translations, element)
|
||||
this.translateNode(translations, element);
|
||||
}
|
||||
|
||||
function asyncForEach(list, iterator, cb) {
|
||||
var i = 0
|
||||
, n = list.length
|
||||
, n = list.length;
|
||||
iterator(list[i], i, function each(err) {
|
||||
if(err) consoleLog(err)
|
||||
i++
|
||||
if(err) consoleLog(err);
|
||||
i++;
|
||||
if (i < n) return iterator(list[i],i, each);
|
||||
cb()
|
||||
cb();
|
||||
})
|
||||
}
|
||||
|
||||
function getTranslatableChildren(element) {
|
||||
if(!document.querySelectorAll) {
|
||||
if (!element) return []
|
||||
if (!element) return [];
|
||||
var nodes = element.getElementsByTagName('*')
|
||||
, l10nElements = []
|
||||
, l10nElements = [];
|
||||
for (var i=0, n=nodes.length; i < n; i++) {
|
||||
if (nodes[i].getAttribute('data-l10n-id'))
|
||||
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) {
|
||||
var translations = html10n.translations
|
||||
if(!translations) return consoleWarn('No translations available (yet)')
|
||||
if(!translations[id]) return consoleWarn('Could not find string '+id)
|
||||
var translations = html10n.translations;
|
||||
if(!translations) return consoleWarn('No translations available (yet)');
|
||||
if(!translations[id]) return consoleWarn('Could not find string '+id);
|
||||
|
||||
// apply args
|
||||
var str = substArguments(translations[id], args)
|
||||
var str = substArguments(translations[id], args);
|
||||
|
||||
// apply macros
|
||||
return substMacros(id, str, args)
|
||||
return substMacros(id, str, args);
|
||||
|
||||
// replace {{arguments}} with their values or the
|
||||
// associated translation string (based on its key)
|
||||
function substArguments(str, args) {
|
||||
var reArgs = /\{\{\s*([a-zA-Z\.]+)\s*\}\}/
|
||||
, match
|
||||
, match;
|
||||
|
||||
while (match = reArgs.exec(str)) {
|
||||
if (!match || match.length < 2)
|
||||
return str // argument key not found
|
||||
return str; // argument key not found
|
||||
|
||||
var arg = match[1]
|
||||
, sub = ''
|
||||
, sub = '';
|
||||
if (arg in args) {
|
||||
sub = args[arg]
|
||||
sub = args[arg];
|
||||
} else if (arg in translations) {
|
||||
sub = translations[arg]
|
||||
sub = translations[arg];
|
||||
} else {
|
||||
consoleWarn('Could not find argument {{' + arg + '}}')
|
||||
return str
|
||||
consoleWarn('Could not find argument {{' + arg + '}}');
|
||||
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
|
||||
|
@ -766,21 +766,21 @@ window.html10n = (function(window, document, undefined) {
|
|||
// a macro has been found
|
||||
// Note: at the moment, only one parameter is supported
|
||||
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) {
|
||||
param = args[paramName]
|
||||
param = args[paramName];
|
||||
} else if (paramName in translations) {
|
||||
param = translations[paramName]
|
||||
param = translations[paramName];
|
||||
}
|
||||
|
||||
// there's no macro parser yet: it has to be defined in gMacros
|
||||
var macro = html10n.macros[macroName]
|
||||
str = macro(translations, key, str, param)
|
||||
return str
|
||||
var macro = html10n.macros[macroName];
|
||||
str = macro(translations, key, str, param);
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -788,26 +788,26 @@ window.html10n = (function(window, document, undefined) {
|
|||
* Applies translations to a DOM node (recursive)
|
||||
*/
|
||||
html10n.translateNode = function(translations, node) {
|
||||
var str = {}
|
||||
var str = {};
|
||||
|
||||
// get id
|
||||
str.id = node.getAttribute('data-l10n-id')
|
||||
if (!str.id) return
|
||||
str.id = node.getAttribute('data-l10n-id');
|
||||
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
|
||||
if(window.JSON) {
|
||||
str.args = JSON.parse(node.getAttribute('data-l10n-args'))
|
||||
str.args = JSON.parse(node.getAttribute('data-l10n-args'));
|
||||
}else{
|
||||
try{
|
||||
str.args = eval(node.getAttribute('data-l10n-args'))
|
||||
str.args = eval(node.getAttribute('data-l10n-args'));
|
||||
}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
|
||||
var prop
|
||||
|
@ -817,31 +817,31 @@ window.html10n = (function(window, document, undefined) {
|
|||
, "innerHTML": 1
|
||||
, "alt": 1
|
||||
, "textContent": 1
|
||||
}
|
||||
};
|
||||
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
|
||||
prop = document.body.textContent ? 'textContent' : 'innerText'
|
||||
prop = document.body.textContent ? 'textContent' : 'innerText';
|
||||
}
|
||||
|
||||
// Apply translation
|
||||
if (node.children.length === 0 || prop != 'textContent') {
|
||||
node[prop] = str.str
|
||||
node[prop] = str.str;
|
||||
} else {
|
||||
var children = node.childNodes,
|
||||
found = false
|
||||
found = false;
|
||||
for (var i=0, n=children.length; i < n; i++) {
|
||||
if (children[i].nodeType === 3 && /\S/.test(children[i].textContent)) {
|
||||
if (!found) {
|
||||
children[i].nodeValue = str.str
|
||||
found = true
|
||||
children[i].nodeValue = str.str;
|
||||
found = true;
|
||||
} else {
|
||||
children[i].nodeValue = ''
|
||||
children[i].nodeValue = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
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) {
|
||||
var that = this
|
||||
, build = {}
|
||||
, build = {};
|
||||
|
||||
asyncForEach(langs, function (lang, i, next) {
|
||||
if(!lang) return next();
|
||||
that.loader.load(lang, next)
|
||||
that.loader.load(lang, next);
|
||||
}, function() {
|
||||
var lang
|
||||
langs.reverse()
|
||||
var lang;
|
||||
langs.reverse();
|
||||
|
||||
// loop through priority array...
|
||||
for (var i=0, n=langs.length; i < n; i++) {
|
||||
lang = langs[i]
|
||||
lang = langs[i];
|
||||
|
||||
if(!lang || !(lang in that.loader.langs)) continue;
|
||||
|
||||
// ... and apply all strings of the current lang in the list
|
||||
// to our build object
|
||||
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
|
||||
// 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
|
||||
*/
|
||||
html10n.getDirection = function() {
|
||||
var langCode = this.language.indexOf('-') == -1? this.language : this.language.substr(0, this.language.indexOf('-'))
|
||||
return html10n.rtl.indexOf(langCode) == -1? 'ltr' : 'rtl'
|
||||
var langCode = this.language.indexOf('-') == -1? this.language : this.language.substr(0, this.language.indexOf('-'));
|
||||
return html10n.rtl.indexOf(langCode) == -1? 'ltr' : 'rtl';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -903,28 +903,28 @@ window.html10n = (function(window, document, undefined) {
|
|||
html10n.index = function () {
|
||||
// Find all <link>s
|
||||
var links = document.getElementsByTagName('link')
|
||||
, resources = []
|
||||
, resources = [];
|
||||
for (var i=0, n=links.length; i < n; i++) {
|
||||
if (links[i].type != 'application/l10n+json')
|
||||
continue;
|
||||
resources.push(links[i].href)
|
||||
resources.push(links[i].href);
|
||||
}
|
||||
this.loader = new Loader(resources)
|
||||
this.trigger('indexed')
|
||||
this.loader = new Loader(resources);
|
||||
this.trigger('indexed');
|
||||
}
|
||||
|
||||
if (document.addEventListener) // modern browsers and IE9+
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
html10n.index()
|
||||
}, false)
|
||||
html10n.index();
|
||||
}, false);
|
||||
else if (window.attachEvent)
|
||||
window.attachEvent('onload', function() {
|
||||
html10n.index()
|
||||
}, false)
|
||||
html10n.index();
|
||||
}, false);
|
||||
|
||||
// gettext-like shortcut
|
||||
if (window._ === undefined)
|
||||
window._ = html10n.get;
|
||||
|
||||
return html10n
|
||||
})(window, document)
|
||||
return html10n;
|
||||
})(window, document);
|
||||
|
|
10833
src/static/js/jquery.js
vendored
10833
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
50
src/static/js/jquery_browser.js
vendored
Normal 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;
|
||||
}
|
||||
})();
|
|
@ -1,8 +1,8 @@
|
|||
(function(document) {
|
||||
// Set language for l10n
|
||||
var language = document.cookie.match(/language=((\w{2,3})(-w+)?)/);
|
||||
var language = document.cookie.match(/language=((\w{2,3})(-\w+)?)/);
|
||||
if(language) language = language[1];
|
||||
|
||||
|
||||
html10n.bind('indexed', function() {
|
||||
html10n.localize([language, navigator.language, navigator.userLanguage, 'en'])
|
||||
})
|
||||
|
|
|
@ -35,7 +35,6 @@ var chat = require('./chat').chat;
|
|||
var getCollabClient = require('./collab_client').getCollabClient;
|
||||
var padconnectionstatus = require('./pad_connectionstatus').padconnectionstatus;
|
||||
var padcookie = require('./pad_cookie').padcookie;
|
||||
var paddocbar = require('./pad_docbar').paddocbar;
|
||||
var padeditbar = require('./pad_editbar').padeditbar;
|
||||
var padeditor = require('./pad_editor').padeditor;
|
||||
var padimpexp = require('./pad_impexp').padimpexp;
|
||||
|
@ -391,10 +390,6 @@ var pad = {
|
|||
{
|
||||
return clientVars.clientIp;
|
||||
},
|
||||
getIsProPad: function()
|
||||
{
|
||||
return clientVars.isProPad;
|
||||
},
|
||||
getColorPalette: function()
|
||||
{
|
||||
return clientVars.colorPalette;
|
||||
|
@ -467,11 +462,6 @@ var pad = {
|
|||
}
|
||||
|
||||
// order of inits is important here:
|
||||
padcookie.init(clientVars.cookiePrefsToSet, this);
|
||||
|
||||
$("#widthprefcheck").click(pad.toggleWidthPref);
|
||||
// $("#sidebarcheck").click(pad.togglewSidebar);
|
||||
|
||||
pad.myUserInfo = {
|
||||
userId: clientVars.userId,
|
||||
name: clientVars.userName,
|
||||
|
@ -488,20 +478,12 @@ var pad = {
|
|||
$("#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);
|
||||
padsavedrevs.init(this);
|
||||
|
||||
padeditor.init(postAceInit, pad.padOptions.view || {}, this);
|
||||
|
||||
paduserlist.init(pad.myUserInfo, this);
|
||||
// padchat.init(clientVars.chatHistory, pad.myUserInfo);
|
||||
padconnectionstatus.init();
|
||||
padmodals.init(this);
|
||||
|
||||
|
@ -540,7 +522,7 @@ var pad = {
|
|||
$('#options-stickychat').prop("checked", true); // set the checkbox to on
|
||||
}
|
||||
if(padcookie.getPref("showAuthorshipColors") == false){
|
||||
pad.changeViewOption('showAuthorColors', false);
|
||||
pad.changeViewOption('showAuthorColors', false);
|
||||
}
|
||||
hooks.aCallAll("postAceInit", {ace: padeditor.ace});
|
||||
}
|
||||
|
@ -553,31 +535,11 @@ var pad = {
|
|||
{
|
||||
pad.myUserInfo.name = newName;
|
||||
pad.collabClient.updateUserInfo(pad.myUserInfo);
|
||||
//padchat.handleUserJoinOrUpdate(pad.myUserInfo);
|
||||
},
|
||||
notifyChangeColor: function(newColorId)
|
||||
{
|
||||
pad.myUserInfo.colorId = newColorId;
|
||||
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)
|
||||
{
|
||||
|
@ -619,7 +581,6 @@ var pad = {
|
|||
{
|
||||
// order important here
|
||||
pad.padOptions.guestPolicy = opts.guestPolicy;
|
||||
paddocbar.setGuestPolicy(opts.guestPolicy);
|
||||
}
|
||||
},
|
||||
getPadOptions: function()
|
||||
|
@ -629,7 +590,7 @@ var pad = {
|
|||
},
|
||||
isPadPublic: function()
|
||||
{
|
||||
return (!pad.getIsProPad()) || (pad.getPadOptions().guestPolicy == 'allow');
|
||||
return pad.getPadOptions().guestPolicy == 'allow';
|
||||
},
|
||||
suggestUserName: function(userId, name)
|
||||
{
|
||||
|
@ -643,17 +604,14 @@ var pad = {
|
|||
handleUserJoin: function(userInfo)
|
||||
{
|
||||
paduserlist.userJoinOrUpdate(userInfo);
|
||||
//padchat.handleUserJoinOrUpdate(userInfo);
|
||||
},
|
||||
handleUserUpdate: function(userInfo)
|
||||
{
|
||||
paduserlist.userJoinOrUpdate(userInfo);
|
||||
//padchat.handleUserJoinOrUpdate(userInfo);
|
||||
},
|
||||
handleUserLeave: function(userInfo)
|
||||
{
|
||||
paduserlist.userLeave(userInfo);
|
||||
//padchat.handleUserLeave(userInfo);
|
||||
},
|
||||
handleClientMessage: function(msg)
|
||||
{
|
||||
|
@ -665,18 +623,6 @@ var pad = {
|
|||
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')
|
||||
{
|
||||
padsavedrevs.newRevisionList(msg.revisionList);
|
||||
|
@ -769,7 +715,6 @@ var pad = {
|
|||
}
|
||||
padeditor.disable();
|
||||
padeditbar.disable();
|
||||
paddocbar.disable();
|
||||
padimpexp.disable();
|
||||
|
||||
padconnectionstatus.disconnected(message);
|
||||
|
@ -796,28 +741,10 @@ var pad = {
|
|||
}, 1000);
|
||||
}
|
||||
|
||||
// pad.determineSidebarVisibility(isConnected && !isInitialConnect);
|
||||
pad.determineChatVisibility(isConnected && !isInitialConnect);
|
||||
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){
|
||||
var chatVisCookie = padcookie.getPref('chatAlwaysVisible');
|
||||
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').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:
|
||||
handleImportExportFrameCall: function(callName, varargs)
|
||||
{
|
||||
|
|
|
@ -43,9 +43,8 @@ var padconnectionstatus = (function()
|
|||
status = {
|
||||
what: 'connected'
|
||||
};
|
||||
|
||||
padmodals.showModal('connected');
|
||||
padmodals.hideOverlay(500);
|
||||
padmodals.hideOverlay();
|
||||
},
|
||||
reconnecting: function()
|
||||
{
|
||||
|
@ -54,7 +53,7 @@ var padconnectionstatus = (function()
|
|||
};
|
||||
|
||||
padmodals.showModal('reconnecting');
|
||||
padmodals.showOverlay(500);
|
||||
padmodals.showOverlay();
|
||||
},
|
||||
disconnected: function(msg)
|
||||
{
|
||||
|
@ -73,10 +72,11 @@ var padconnectionstatus = (function()
|
|||
}
|
||||
|
||||
padmodals.showModal(k);
|
||||
padmodals.showOverlay(500);
|
||||
padmodals.showOverlay();
|
||||
},
|
||||
isFullyConnected: function()
|
||||
{
|
||||
padmodals.hideOverlay();
|
||||
return status.what == 'connected';
|
||||
},
|
||||
getStatus: function()
|
||||
|
|
|
@ -73,7 +73,7 @@ var padcookie = (function()
|
|||
}
|
||||
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.");
|
||||
alreadyWarnedAboutNoCookies = true;
|
||||
|
|
|
@ -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, '•'));
|
||||
}
|
||||
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;
|
|
@ -156,10 +156,7 @@ var padeditbar = (function()
|
|||
else if (cmd == 'insertorderedlist') ace.ace_doInsertOrderedList();
|
||||
else if (cmd == 'indent')
|
||||
{
|
||||
if (!ace.ace_doIndentOutdent(false))
|
||||
{
|
||||
ace.ace_doInsertUnorderedList();
|
||||
}
|
||||
ace.ace_doIndentOutdent(false);
|
||||
}
|
||||
else if (cmd == 'outdent')
|
||||
{
|
||||
|
|
|
@ -20,14 +20,11 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
var paddocbar = require('./pad_docbar').paddocbar;
|
||||
|
||||
var padimpexp = (function()
|
||||
{
|
||||
|
||||
///// import
|
||||
var currentImportTimer = null;
|
||||
var hidePanelCall = null;
|
||||
|
||||
function addImportFrames()
|
||||
{
|
||||
|
@ -72,7 +69,6 @@ var padimpexp = (function()
|
|||
var ret = window.confirm(html10n.get("pad.impexp.confirmimport"));
|
||||
if (ret)
|
||||
{
|
||||
hidePanelCall = paddocbar.hideLaterIfNoOtherInteraction();
|
||||
currentImportTimer = window.setTimeout(function()
|
||||
{
|
||||
if (!currentImportTimer)
|
||||
|
@ -255,11 +251,6 @@ var padimpexp = (function()
|
|||
$("#exportopena").attr("href", pad_root_path + "/export/odt");
|
||||
}
|
||||
|
||||
$("#impexp-close").click(function()
|
||||
{
|
||||
paddocbar.setShownPanel(null);
|
||||
});
|
||||
|
||||
addImportFrames();
|
||||
$("#importfileinput").change(fileInputUpdated);
|
||||
$('#importform').submit(fileInputSubmit);
|
||||
|
|
|
@ -107,14 +107,9 @@ var paduserlist = (function()
|
|||
function getUserRowHtml(height, data)
|
||||
{
|
||||
var nameHtml;
|
||||
var isGuest = (data.id.charAt(0) != 'p');
|
||||
if (data.name)
|
||||
{
|
||||
nameHtml = padutils.escapeHtml(data.name);
|
||||
if (isGuest && pad.getIsProPad())
|
||||
{
|
||||
nameHtml += ' ('+_(pad.userlist.guest)+')';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -520,11 +520,11 @@ function setupGlobalExceptionHandler() {
|
|||
$("#editorloadingbox").css("padding", "10px");
|
||||
$("#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'>'"
|
||||
+ "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
|
||||
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 url = loc.protocol + "//" + loc.hostname + ":" + loc.port + "/" + loc.pathname.substr(1, loc.pathname.indexOf("/p/")) + "jserror";
|
||||
|
||||
|
|
|
@ -70,10 +70,12 @@ exports.flatten = function (lst) {
|
|||
|
||||
exports.callAll = function (hook_name, args) {
|
||||
if (!args) args = {};
|
||||
if (exports.plugins.hooks[hook_name] === undefined) return [];
|
||||
return _.flatten(_.map(exports.plugins.hooks[hook_name], function (hook) {
|
||||
return hookCallWrapper(hook, hook_name, args);
|
||||
}), true);
|
||||
if (exports.plugins){
|
||||
if (exports.plugins.hooks[hook_name] === undefined) return [];
|
||||
return _.flatten(_.map(exports.plugins.hooks[hook_name], function (hook) {
|
||||
return hookCallWrapper(hook, hook_name, args);
|
||||
}), true);
|
||||
}
|
||||
}
|
||||
|
||||
exports.aCallAll = function (hook_name, args, cb) {
|
||||
|
|
|
@ -94,11 +94,12 @@ exports.search = function(query, cache, cb) {
|
|||
if (er) return cb(er);
|
||||
var res = {};
|
||||
var i = 0;
|
||||
var pattern = query.pattern.toLowerCase();
|
||||
for (key in data) { // for every plugin in the data from npm
|
||||
if ( key.indexOf(plugins.prefix) == 0
|
||||
&& key.indexOf(query.pattern) != -1
|
||||
&& key.indexOf(pattern) != -1
|
||||
|| 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
|
||||
i++;
|
||||
if (i > query.offset
|
||||
|
|
|
@ -2,4 +2,9 @@
|
|||
// Proviedes a require'able version of jQuery without leaking $ and 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;
|
|
@ -29,8 +29,9 @@ var createCookie = require('./pad_utils').createCookie;
|
|||
var readCookie = require('./pad_utils').readCookie;
|
||||
var randomString = require('./pad_utils').randomString;
|
||||
var _ = require('./underscore');
|
||||
var hooks = require('./pluginfw/hooks');
|
||||
|
||||
var socket, token, padId, export_links;
|
||||
var token, padId, export_links;
|
||||
|
||||
function init() {
|
||||
$(document).ready(function ()
|
||||
|
@ -106,6 +107,9 @@ function init() {
|
|||
window.location.reload();
|
||||
});
|
||||
|
||||
exports.socket = socket; // make the socket available
|
||||
|
||||
hooks.aCallAll("postTimesliderInit");
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Admin Dashboard - Etherpad lite</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">
|
||||
<title>Admin Dashboard - Etherpad</title>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<link rel="stylesheet" href="../static/css/admin.css">
|
||||
<script src="../static/js/jquery.js"></script>
|
||||
<script src="../socket.io/socket.io.js"></script>
|
||||
|
@ -9,13 +10,15 @@
|
|||
<body>
|
||||
<div id="wrapper">
|
||||
<div class="menu">
|
||||
<h1>Etherpad lite</h1>
|
||||
<li><a href="admin/plugins">Plugin manager</a> </li>
|
||||
<li><a href="admin/settings">Settings</a> </li>
|
||||
<li><a href="admin/plugins/info">Troubleshooting information</a> </li>
|
||||
<h1>Etherpad</h1>
|
||||
<ul>
|
||||
<% e.begin_block("adminMenu"); %>
|
||||
<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 id="topborder"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,20 +1,24 @@
|
|||
<%
|
||||
var plugins = require("ep_etherpad-lite/static/js/pluginfw/plugins");
|
||||
%>
|
||||
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Plugin information - Etherpad lite</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">
|
||||
<title>Plugin information - Etherpad</title>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<link rel="stylesheet" href="../../static/css/admin.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="wrapper">
|
||||
<div class="menu">
|
||||
<h1>Etherpad lite</h1>
|
||||
<li><a href="../plugins">Plugin manager</a> </li>
|
||||
<li><a href="../settings">Settings</a> </li>
|
||||
<li><a href="../plugins/info">Troubleshooting information</a> </li>
|
||||
<h1>Etherpad</h1>
|
||||
<ul>
|
||||
<% e.begin_block("adminMenu"); %>
|
||||
<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 class="innerwrapper">
|
||||
|
@ -34,6 +38,5 @@
|
|||
|
||||
</div>
|
||||
</div>
|
||||
<div id="topborder"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Plugin manager - Etherpad lite</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">
|
||||
<title>Plugin manager - Etherpad</title>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<link rel="stylesheet" href="../static/css/admin.css">
|
||||
<script src="../static/js/jquery.js"></script>
|
||||
<script src="../socket.io/socket.io.js"></script>
|
||||
|
@ -19,12 +20,15 @@
|
|||
<% } %>
|
||||
|
||||
<div class="menu">
|
||||
<h1>Etherpad lite</h1>
|
||||
<li><a href="plugins">Plugin manager</a> </li>
|
||||
<li><a href="settings">Settings</a> </li>
|
||||
<li><a href="plugins/info">Troubleshooting information</a> </li>
|
||||
|
||||
<div id="progress"><img src="../static/img/loading.gif" alt=""/> <span class="message"></span></div>
|
||||
<h1>Etherpad</h1>
|
||||
<ul>
|
||||
<% e.begin_block("adminMenu"); %>
|
||||
<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 id="progress"><img src="../static/img/loading.gif"> <span class="message"></span></div>
|
||||
</div>
|
||||
|
||||
<div class="innerwrapper">
|
||||
|
@ -40,9 +44,9 @@
|
|||
</thead>
|
||||
<tbody class="template">
|
||||
<tr id="installed-plugin-template">
|
||||
<td class="name"></td>
|
||||
<td class="description"></td>
|
||||
<td class="version"></td>
|
||||
<td class="name" data-label="Name"></td>
|
||||
<td class="description" data-label="Description"></td>
|
||||
<td class="version" data-label="Version"></td>
|
||||
<td class="actions">
|
||||
<input type="button" value="Uninstall" class="do-uninstall">
|
||||
</td>
|
||||
|
@ -71,9 +75,9 @@
|
|||
</thead>
|
||||
<tbody class="template">
|
||||
<tr>
|
||||
<td class="name"></td>
|
||||
<td class="description"></td>
|
||||
<td class="version"></td>
|
||||
<td class="name" data-label="Name"></td>
|
||||
<td class="description" data-label="Description"></td>
|
||||
<td class="version" data-label="Version"></td>
|
||||
<td class="actions">
|
||||
<input type="button" value="Install" class="do-install">
|
||||
</td>
|
||||
|
@ -89,6 +93,5 @@
|
|||
|
||||
</div>
|
||||
</div>
|
||||
<div id="topborder"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Settings - Etherpad lite</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">
|
||||
<title>Settings - Etherpad</title>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<link rel="stylesheet" href="../static/css/admin.css">
|
||||
<script src="../static/js/jquery.js"></script>
|
||||
<script src="../socket.io/socket.io.js"></script>
|
||||
|
@ -23,22 +24,26 @@
|
|||
|
||||
|
||||
<div class="menu">
|
||||
<h1>Etherpad lite</h1>
|
||||
<li><a href="plugins">Plugin manager</a> </li>
|
||||
<li><a href="settings">Settings</a> </li>
|
||||
<li><a href="plugins/info">Troubleshooting information</a> </li>
|
||||
<h1>Etherpad</h1>
|
||||
<ul>
|
||||
<% e.begin_block("adminMenu"); %>
|
||||
<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 class="innerwrapper">
|
||||
<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>
|
||||
<h2>Current configuration</h2>
|
||||
<textarea class="settings"></textarea>
|
||||
<input type="button" class="settingsButton" id="saveSettings" value="Save Settings">
|
||||
<input type="button" class="settingsButton" id="restartEtherpad" value="Restart Etherpad">
|
||||
<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 id="topborder"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -64,7 +64,8 @@
|
|||
box-shadow: 0px 1px 8px rgba(0,0,0,0.3);
|
||||
}
|
||||
#inner {
|
||||
width: 300px;
|
||||
position:relative;
|
||||
max-width: 300px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
#button {
|
||||
|
@ -100,6 +101,10 @@
|
|||
text-shadow: 0 1px 1px #fff;
|
||||
margin: 16px auto 0;
|
||||
}
|
||||
#padname{
|
||||
height:38px;
|
||||
max-width:280px;
|
||||
}
|
||||
form {
|
||||
height: 38px;
|
||||
background: #fff;
|
||||
|
@ -115,7 +120,8 @@
|
|||
border-radius: 3px;
|
||||
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 */
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
@ -125,7 +131,7 @@
|
|||
}
|
||||
button[type="submit"] {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
left:253px;
|
||||
width: 45px;
|
||||
height: 38px;
|
||||
}
|
||||
|
|
|
@ -40,8 +40,10 @@
|
|||
<% e.end_block(); %>
|
||||
|
||||
<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/l10n.js"></script>
|
||||
<% e.end_block(); %>
|
||||
</head>
|
||||
|
||||
<% e.begin_block("timesliderBody"); %>
|
||||
|
@ -211,6 +213,8 @@
|
|||
}
|
||||
|
||||
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.update(function () {
|
||||
|
|
|
@ -85,8 +85,8 @@ var helper = {};
|
|||
return !$iframe.contents().find("#editorloadingbox").is(":visible");
|
||||
}, 50000).done(function(){
|
||||
helper.padChrome$ = getFrameJQuery( $('#iframe-container iframe'));
|
||||
helper.padOuter$ = getFrameJQuery(helper.padChrome$('iframe.[name="ace_outer"]'));
|
||||
helper.padInner$ = getFrameJQuery( helper.padOuter$('iframe.[name="ace_inner"]'));
|
||||
helper.padOuter$ = getFrameJQuery(helper.padChrome$('iframe[name="ace_outer"]'));
|
||||
helper.padInner$ = getFrameJQuery( helper.padOuter$('iframe[name="ace_inner"]'));
|
||||
|
||||
//disable all animations, this makes tests faster and easier
|
||||
helper.padChrome$.fx.off = true;
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
<div id="iframe-container"></div>
|
||||
|
||||
<script src="/static/js/jquery.js"></script>
|
||||
<script src="/static/js/jquery_browser.js"></script>
|
||||
<script src="lib/underscore.js"></script>
|
||||
|
||||
<script src="lib/mocha.js"></script>
|
||||
|
|
|
@ -179,7 +179,11 @@ $(function(){
|
|||
//inject spec scripts into the dom
|
||||
var $body = $('body');
|
||||
$.each(specs, function(i, spec){
|
||||
$body.append('<script src="specs/' + spec + '"></script>')
|
||||
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>')
|
||||
}else{
|
||||
$body.append('<script src="' + spec + '"></script>')
|
||||
}
|
||||
});
|
||||
|
||||
//initalize the test helper
|
||||
|
@ -196,4 +200,4 @@ $(function(){
|
|||
|
||||
mocha.run();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -100,8 +100,8 @@ describe("embed links", function(){
|
|||
|
||||
//open share dropdown
|
||||
chrome$(".buttonicon-embed").click();
|
||||
//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
|
||||
var shareLink = chrome$("#linkinput").val();
|
||||
|
@ -119,7 +119,9 @@ describe("embed links", function(){
|
|||
//open share dropdown
|
||||
chrome$(".buttonicon-embed").click();
|
||||
//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
|
||||
var embedCode = chrome$("#embedinput").val();
|
||||
|
@ -129,5 +131,6 @@ describe("embed links", function(){
|
|||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
|
|
@ -19,6 +19,7 @@ describe("font select", function(){
|
|||
|
||||
//select monospace and fire change event
|
||||
$monospaceoption.attr('selected','selected');
|
||||
$viewfontmenu.val("monospace");
|
||||
$viewfontmenu.change();
|
||||
|
||||
//check if font changed to monospace
|
||||
|
|
|
@ -56,10 +56,8 @@ describe("Language select and change", function(){
|
|||
|
||||
//click the language button
|
||||
var $language = chrome$("#languagemenu");
|
||||
var $languageoption = $language.find("[value=en]");
|
||||
|
||||
//select german
|
||||
$languageoption.attr('selected','selected');
|
||||
//select english
|
||||
$language.val("en");
|
||||
$language.change();
|
||||
|
||||
//get the value of the bold button
|
||||
|
|
64
tests/frontend/specs/timeslider_labels.js
Normal file
64
tests/frontend/specs/timeslider_labels.js
Normal 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);
|
||||
});
|
||||
});
|
|
@ -4,8 +4,7 @@ describe("timeslider", function(){
|
|||
helper.newPad(cb);
|
||||
this.timeout(6000);
|
||||
});
|
||||
|
||||
xit("loads adds a hundred revisions", function(done) {
|
||||
it("loads adds a hundred revisions", function(done) {
|
||||
var inner$ = helper.padInner$;
|
||||
var chrome$ = helper.padChrome$;
|
||||
|
||||
|
@ -57,14 +56,13 @@ describe("timeslider", function(){
|
|||
}, 6000);
|
||||
}, revs*timePerRev);
|
||||
});
|
||||
|
||||
it("changes the url when clicking on the timeslider", function(done) {
|
||||
var inner$ = helper.padInner$;
|
||||
var chrome$ = helper.padChrome$;
|
||||
|
||||
// make some changes to produce 7 revisions
|
||||
var timePerRev = 900
|
||||
, revs = 7;
|
||||
var timePerRev = 1000
|
||||
, revs = 20;
|
||||
this.timeout(revs*timePerRev+10000);
|
||||
for(var i=0; i < revs; i++) {
|
||||
setTimeout(function() {
|
||||
|
@ -100,28 +98,48 @@ describe("timeslider", function(){
|
|||
}, 6000);
|
||||
}, revs*timePerRev);
|
||||
});
|
||||
|
||||
it("jumps to a revision given in the url", function(done) {
|
||||
var inner$ = helper.padInner$;
|
||||
var chrome$ = helper.padChrome$;
|
||||
this.timeout(11000);
|
||||
inner$("div").first().sendkeys('a');
|
||||
|
||||
setTimeout(function() {
|
||||
// go to timeslider
|
||||
$('#iframe-container iframe').attr('src', $('#iframe-container iframe').attr('src')+'/timeslider#0');
|
||||
var timeslider$;
|
||||
var chrome$ = helper.padChrome$;
|
||||
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');
|
||||
|
||||
// wait for our additional revision to be added
|
||||
helper.waitFor(function(){
|
||||
timeslider$ = $('#iframe-container iframe')[0].contentWindow.$;
|
||||
return timeslider$ && timeslider$('#padcontent').text().length == 230;
|
||||
}, 6000).always(function(){
|
||||
expect( timeslider$('#padcontent').text().length ).to.eql( 230 );
|
||||
done();
|
||||
// newLines takes the new lines into account which are strippen when using
|
||||
// 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() {
|
||||
// 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();
|
||||
});
|
||||
});
|
||||
}, 2500);
|
||||
});
|
||||
});
|
||||
|
||||
it("checks the export url", function(done) {
|
||||
var inner$ = helper.padInner$;
|
||||
var chrome$ = helper.padChrome$;
|
||||
|
@ -135,7 +153,9 @@ describe("timeslider", function(){
|
|||
var exportLink;
|
||||
|
||||
helper.waitFor(function(){
|
||||
timeslider$ = $('#iframe-container iframe')[0].contentWindow.$;
|
||||
try{
|
||||
timeslider$ = $('#iframe-container iframe')[0].contentWindow.$;
|
||||
}catch(e){}
|
||||
if(!timeslider$)
|
||||
return false;
|
||||
exportLink = timeslider$('#exportplaina').attr('href');
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue