Release version 1.7.0

From now on, releases will be cut from develop, and merged directly into master.

Each release will be a tag on the master branch (e.g. 1.7.0).
A "release/1.7.0" branch will eventually be created only if/when a hotfix will
be needed.
This commit is contained in:
muxator 2018-08-17 00:43:01 +02:00
commit 96ac381afb
47 changed files with 865 additions and 392 deletions

View file

@ -1,3 +1,13 @@
# 1.7.0
* FIX: `getLineHTMLForExport()` no longer produces multiple copies of a line. **WARNING**: this could potentially break some plugins
* FIX: authorship of bullet points no longer changes when a second author edits them
* FIX: improved Firefox compatibility (non printable keys)
* FIX: `getPadPlainText()` was not working
* REQUIREMENTS: minimum required Node version is 6.9.0 LTS. The next release will require at least Node 8.9.0 LTS
* SECURITY: updated MySQL, Elasticsearch and PostgreSQL drivers
* SECURITY: started updating deprecated code and packages
* DOCS: documented --credentials, --apikey, --sessionkey. Better detailed contributors guidelines. Added a section on securing the installation
# 1.6.6
* FIX: line numbers are aligned with text again (broken in 1.6.4)
* FIX: text entered between connection loss and reconnection was not saved
@ -490,7 +500,7 @@
* Plugin-specific settings in settings.json (finally allowing for things like a google analytics plugin)
* Serve admin dashboard at /admin (still very limited, though)
* Modify your settings.json through the newly created UI at /admin/settings
* Fix: Import <ol>'s as <ol>'s and not as <ul>'s!
* Fix: Import `<ol>` as `<ol>` and not as `<ul>`!
* Added solaris compatibility (bin/installDeps.sh was broken on solaris)
* Fix a bug with IE9 and Password Protected Pads using HTTPS

View file

@ -1,6 +1,34 @@
# Contributor Guidelines
(Please talk to people on the mailing list before you change this page, see our section on [how to get in touch](https://github.com/ether/etherpad-lite#get-in-touch))
## Pull requests
* the commit series in the PR should be _linear_ (it **should not contain merge commits**). This is necessary because we want to be able to [bisect](https://en.wikipedia.org/wiki/Bisection_(software_engineering)) bugs easily. Rewrite history/perform a rebase if necessary
* PRs should be issued against the **develop** branch: we never pull directly into **master**
* PRs **should not have conflicts** with develop. If there are, please resolve them rebasing and force-pushing
* when preparing your PR, please make sure that you have included the relevant **changes to the documentation** (preferably with usage examples)
* contain meaningful and detailed **commit messages** in the form:
```
submodule: description
longer description of the change you have made, eventually mentioning the
number of the issue that is being fixed, in the form: Fixes #someIssueNumber
```
* if the PR is a **bug fix**:
* the first commit in the series must be a test that shows the failure
* subsequent commits will fix the bug and make the test pass
* the final commit message should include the text `Fixes: #xxx` to link it to its bug report
* think about stability: code has to be backwards compatible as much as possible. Always **assume your code will be run with an older version of the DB/config file**
* if you want to remove a feature, **deprecate it instead**:
* write an issue with your deprecation plan
* output a `WARN` in the log informing that the feature is going to be removed
* remove the feature in the next version
* if you want to add a new feature, put it under a **feature flag**:
* once the new feature has reached a minimal level of stability, do a PR for it, so it can be integrated early
* expose a mechanism for enabling/disabling the feature
* the new feature should be **disabled** by default. With the feature disabled, the code path should be exactly the same as before your contribution. This is a __necessary condition__ for early integration
* think of the PR not as something that __you wrote__, but as something that __someone else is going to read__. The commit series in the PR should tell a novice developer the story of your thoughts when developing it
## How to write a bug report
* Please be polite, we all are humans and problems can occur.
@ -25,12 +53,6 @@ If you send logfiles, please set the loglevel switch DEBUG in your settings.json
The logfile location is defined in startup script or the log is directly shown in the commandline after you have started etherpad.
## Important note for pull requests
**Pull requests should be issued against the develop branch**. We never pull directly into master.
**Our goal is to iterate in small steps. Release often, release early. Evolution instead of a revolution**
## General goals of Etherpad
To make sure everybody is going in the same direction:
* easy to install for admins and easy to use for people
@ -93,6 +115,8 @@ You can build the docs e.g. produce html, using `make docs`. At some point in th
## Testing
Front-end tests are found in the `tests/frontend/` folder in the repository. Run them by pointing your browser to `<yourdomainhere>/tests/frontend`.
Back-end tests can be run from the `src` directory, via `npm test`.
## Things you can help with
Etherpad is much more than software. So if you aren't a developer then worry not, there is still a LOT you can do! A big part of what we do is community engagement. You can help in the following ways
* Triage bugs (applying labels) and confirming their existance

View file

@ -1,8 +1,3 @@
### This project is looking for a new project lead. If you wish to help steer Etherpad forward please email contact@etherpad.org
[![Deps](https://david-dm.org/ether/etherpad-lite.svg?branch=develop)](https://david-dm.org/ether/etherpad-lite)
[![NSP Status](https://nodesecurity.io/orgs/etherpad/projects/635f6185-35c6-4ed7-931a-0bc62758ece7/badge)](https://nodesecurity.io/orgs/etherpad/projects/635f6185-35c6-4ed7-931a-0bc62758ece7)
# A really-real time collaborative word processor for the web
![Demo Etherpad Animated Jif](https://i.imgur.com/zYrGkg3.gif "Etherpad in action on PrimaryPad")
@ -13,6 +8,9 @@ Etherpad is a really-real time collaborative editor scalable to thousands of sim
# Installation
## Requirements
- `nodejs` >= **6.9.0** (preferred: `nodejs` >= **8.9**)
## Uber-Quick Ubuntu
```
curl -sL https://deb.nodesource.com/setup_9.x | sudo -E bash -
@ -26,25 +24,26 @@ You'll need gzip, git, curl, libssl develop libraries, python and gcc.
- *For Fedora/CentOS*: `yum install gzip git curl python openssl-devel && yum groupinstall "Development Tools"`
- *For FreeBSD*: `portinstall node, npm, curl, git (optional)`
Additionally, you'll need [node.js](https://nodejs.org) installed, Ideally the latest stable version, we recommend installing/compiling nodejs from source (avoiding apt).
Additionally, you'll need [node.js](https://nodejs.org) installed (minimum required Node version: **6.9.0**).
Ideally, the latest stable version is preferred. Please note that the packages offered on some operating systems are outdated. In those cases, we recommend installing nodejs from official archives or compiling it from source (avoiding yum/apt).
**As any user (we recommend creating a separate user called etherpad):**
1. Move to a folder where you want to install Etherpad. Clone the git repository `git clone git://github.com/ether/etherpad-lite.git`
2. Change into the new directory containing the cloned source code `cd etherpad-lite`
1. Move to a folder where you want to install Etherpad. Clone the git repository: `git clone git://github.com/ether/etherpad-lite.git`
2. Change into the new directory containing the cloned source code: `cd etherpad-lite`
Now, run `bin/run.sh` and open <http://127.0.0.1:9001> in your browser.
Update to the latest version with `git pull origin`. The next start with bin/run.sh will update the dependencies.
Update to the latest version with `git pull origin`. The next start with `bin/run.sh` will update the dependencies.
[Next steps](#next-steps).
## Windows
### Prebuilt windows package
### Prebuilt Windows package
This package works out of the box on any windows machine, but it's not very useful for developing purposes...
1. [Download the latest windows package](http://etherpad.org/#download)
1. [Download the latest Windows package](http://etherpad.org/#download)
2. Extract the folder
Now, run `start.bat` and open <http://localhost:9001> in your browser. You like it? [Next steps](#next-steps).
@ -63,17 +62,26 @@ Update to the latest version with `git pull origin`, then run `bin\installOnWind
If cloning to a subdirectory within another project, you may need to do the following:
1. Start the server manually (e.g. `node/node_modules/ep_etherpad-lite/node/server.js]`)
1. Start the server manually (e.g. `node/node_modules/ep_etherpad-lite/node/server.js`)
2. Edit the db `filename` in `settings.json` to the relative directory with the file (e.g. `application/lib/etherpad-lite/var/dirty.db`)
3. Add auto-generated files to the main project `.gitignore`
# Next Steps
## Tweak the settings
You can initially modify the settings in `settings.json`. (If you need to handle multiple settings files, you can pass the path to a settings file to `bin/run.sh` using the `-s|--settings` option. This allows you to run multiple Etherpad instances from the same installation.) Once you have access to your /admin section settings can be modified through the web browser.
You can modify the settings in `settings.json`.
If you need to handle multiple settings files, you can pass the path to a settings file to `bin/run.sh` using the `-s|--settings` option: this allows you to run multiple Etherpad instances from the same installation.
Similarly, `--credentials` can be used to give a settings override file, `--apikey` to give a different APIKEY.txt file and `--sessionkey` to give a non-default SESSIONKEY.txt.
Once you have access to your /admin section settings can be modified through the web browser.
You should use a dedicated database such as "mysql", if you are planning on using etherpad-in a production environment, since the "dirtyDB" database driver is only for testing and/or development purposes.
## Secure your installation
If you have enabled authentication in `users` section in `settings.json`, it is a good security practice to **store hashes instead of plain text passwords** in that file. This is _especially_ advised if you are running a production installation.
Please install [ep_hash_auth plugin](https://www.npmjs.com/package/ep_hash_auth) and configure it.
If you prefer, `ep_hash_auth` also gives you the option of storing the users in a custom directory in the file system, without having to edit `settings.json` and restart Etherpad each time.
## Plugins and themes
Etherpad is very customizable through plugins. Instructions for installing themes and plugins can be found in [the plugin wiki article](https://github.com/ether/etherpad-lite/wiki/Available-Plugins).

View file

@ -1 +0,0 @@
src/node_modules/mocha/bin/mocha --timeout 5000 --reporter nyan tests/backend/specs/api

View file

@ -1,5 +1,10 @@
#!/bin/bash
#
# WARNING: since Etherpad 1.7.0 (2018-08-17), this script is DEPRECATED, and
# will be removed/modified in a future version.
# It's left here just for documentation.
# The branching policies for releases have been changed.
#
# This script is used to publish a new release/version of etherpad on github
#
# Work that is done by this script:
@ -16,6 +21,16 @@
# ETHER_REPO:
# - Create a new release on github
printf "WARNING: since Etherpad 1.7.0 this script is DEPRECATED, and will be removed/modified in a future version.\n\n"
while true; do
read -p "Do you want to continue? This is discouraged. [y/N]" yn
case $yn in
[Yy]* ) break;;
[Nn]* ) exit;;
* ) printf "Please answer yes or no.\n\n";;
esac
done
ETHER_REPO="https://github.com/ether/etherpad-lite.git"
ETHER_WEB_REPO="https://github.com/ether/ether.github.com.git"
TMP_DIR="/tmp/"

View file

@ -1,5 +1,53 @@
#!/bin/sh
# minimum required node version
REQUIRED_NODE_MAJOR=6
REQUIRED_NODE_MINOR=9
# minimum required npm version
REQUIRED_NPM_MAJOR=3
REQUIRED_NPM_MINOR=10
require_minimal_version() {
PROGRAM_LABEL="$1"
VERSION_STRING="$2"
REQUIRED_MAJOR="$3"
REQUIRED_MINOR="$4"
# Flag -s (--only-delimited on GNU cut) ensures no string is returned
# when there is no match
DETECTED_MAJOR=$(echo $VERSION_STRING | cut -s -d "." -f 1)
DETECTED_MINOR=$(echo $VERSION_STRING | cut -s -d "." -f 2)
if [ -z "$DETECTED_MAJOR" ]; then
printf 'Cannot extract %s major version from version string "%s"\n' "$PROGRAM_LABEL" "$VERSION_STRING" >&2
exit 1
fi
if [ -z "$DETECTED_MINOR" ]; then
printf 'Cannot extract %s minor version from version string "%s"\n' "$PROGRAM_LABEL" "$VERSION_STRING" >&2
exit 1
fi
case "$DETECTED_MAJOR" in
''|*[!0-9]*)
printf '%s major version from "%s" is not a number. Detected: "%s"\n' "$PROGRAM_LABEL" "$VERSION_STRING" "$DETECTED_MAJOR" >&2
exit 1
;;
esac
case "$DETECTED_MINOR" in
''|*[!0-9]*)
printf '%s minor version from "%s" is not a number. Detected: "%s"\n' "$PROGRAM_LABEL" "$VERSION_STRING" "$DETECTED_MINOR" >&2
exit 1
esac
if [ "$DETECTED_MAJOR" -lt "$REQUIRED_MAJOR" ] || ([ "$DETECTED_MAJOR" -eq "$REQUIRED_MAJOR" ] && [ "$DETECTED_MINOR" -lt "$REQUIRED_MINOR" ]); then
printf 'Your %s version "%s" is too old. %s %d.%d.x or higher is required.\n' "$PROGRAM_LABEL" "$VERSION_STRING" "$PROGRAM_LABEL" "$REQUIRED_MAJOR" "$REQUIRED_MINOR" >&2
exit 1
fi
}
#Move to the folder where ep-lite is installed
cd `dirname $0`
@ -36,22 +84,15 @@ hash npm > /dev/null 2>&1 || {
}
#Check npm version
NPM_VERSION=$(npm --version)
NPM_MAIN_VERSION=$(echo $NPM_VERSION | cut -d "." -f 1)
if [ $(echo $NPM_MAIN_VERSION) = "0" ]; then
echo "You're running a wrong version of npm, you're using $NPM_VERSION, we need 1.x or higher" >&2
exit 1
fi
NPM_VERSION_STRING=$(npm --version)
require_minimal_version "npm" "$NPM_VERSION_STRING" "$REQUIRED_NPM_MAJOR" "$REQUIRED_NPM_MINOR"
#Check node version
NODE_VERSION=$(node --version)
NODE_V_MINOR=$(echo $NODE_VERSION | cut -d "." -f 1-2)
NODE_V_MAIN=$(echo $NODE_VERSION | cut -d "." -f 1)
NODE_V_MAIN=${NODE_V_MAIN#"v"}
if [ ! $NODE_V_MINOR = "v0.10" ] && [ ! $NODE_V_MINOR = "v0.11" ] && [ ! $NODE_V_MINOR = "v0.12" ] && [ ! $NODE_V_MAIN -ge 4 ]; then
echo "You're running a wrong version of node. You're using $NODE_VERSION, we need node v0.10.x or higher" >&2
exit 1
fi
NODE_VERSION_STRING=$(node --version)
NODE_VERSION_STRING=${NODE_VERSION_STRING#"v"}
require_minimal_version "nodejs" "$NODE_VERSION_STRING" "$REQUIRED_NODE_MAJOR" "$REQUIRED_NODE_MINOR"
#Get the name of the settings file
settings="settings.json"
@ -73,7 +114,7 @@ echo "Ensure that all dependencies are up to date... If this is the first time
cd node_modules
[ -e ep_etherpad-lite ] || ln -s ../src ep_etherpad-lite
cd ep_etherpad-lite
npm install --loglevel warn
npm install --no-save --loglevel warn
) || {
rm -rf node_modules
exit 1

View file

@ -72,7 +72,7 @@ The API is accessible via HTTP. HTTP Requests are in the format /api/$APIVERSION
### Response Format
Responses are valid JSON in the following format:
```js
```json
{
"code": number,
"message": string,

View file

@ -50,7 +50,7 @@ There are server hooks, which will be executed on the server (e.g. `expressCreat
### Parts
As your plugins become more and more complex, you will find yourself in the need to manage dependencies between plugins. E.g. you want the hooks of a certain plugin to be executed before (or after) yours. You can also manage these dependencies in your plugin definition file `ep.json`:
```javascript
```json
{
"parts": [
{
@ -99,7 +99,7 @@ Your plugin must also contain a [package definition file](https://docs.npmjs.com
"author": "USERNAME (REAL NAME) <MAIL@EXAMPLE.COM>",
"contributors": [],
"dependencies": {"MODULE": "0.3.20"},
"engines": { "node": ">= 0.6.0"}
"engines": { "node": ">= 6.9.0"}
}
```

View file

@ -15,4 +15,4 @@ We currently measure:
Under the hood, we are happy to rely on [measured](https://github.com/felixge/node-measured) for all our metrics needs.
To modify or simply access our stats in your plugin, simply `require('ep_etherpad-lite/stats')` which is a `measured.Collection`.
To modify or simply access our stats in your plugin, simply `require('ep_etherpad-lite/stats')` which is a [`measured.Collection`](https://yaorg.github.io/node-measured/packages/measured-core/Collection.html).

View file

@ -1,64 +1,105 @@
/*
This file must be valid JSON. But comments are allowed
Please edit settings.json, not settings.json.template
To still commit settings without credentials you can
store any credential settings in credentials.json
* This file must be valid JSON. But comments are allowed
*
* Please edit settings.json, not settings.json.template
*
* Please note that since Etherpad 1.6.0 you can store DB credentials in a
* separate file (credentials.json).
*/
{
// Name your instance!
/*
* Name your instance!
*/
"title": "Etherpad",
// favicon default name
// alternatively, set up a fully specified Url to your own favicon
/*
* favicon default name
* alternatively, set up a fully specified Url to your own favicon
*/
"favicon": "favicon.ico",
//IP and port which etherpad should bind at
/*
* IP and port which etherpad should bind at
*/
"ip": "0.0.0.0",
"port" : 9001,
// Option to hide/show the settings.json in admin page, default option is set to true
/*
* Option to hide/show the settings.json in admin page.
*
* Default option is set to true
*/
"showSettingsInAdminPage" : true,
/*
// Node native SSL support
// this is disabled by default
//
// make sure to have the minimum and correct file access permissions set
// so that the Etherpad server can access them
* Node native SSL support
*
* This is disabled by default.
* Make sure to have the minimum and correct file access permissions set so
* that the Etherpad server can access them
*/
/*
"ssl" : {
"key" : "/path-to-your/epl-server.key",
"cert" : "/path-to-your/epl-server.crt",
"ca": ["/path-to-your/epl-intermediate-cert1.crt", "/path-to-your/epl-intermediate-cert2.crt"]
},
*/
//The Type of the database. You can choose between dirty, postgres, sqlite and mysql
//You shouldn't use "dirty" for for anything else than testing or development
/*
* The type of the database.
*
* You can choose between many DB drivers, for example: dirty, postgres,
* sqlite, mysql.
*
* You shouldn't use "dirty" for for anything else than testing or
* development.
*
* For a complete list of the supported drivers, please consult:
* https://www.npmjs.com/package/ueberdb2
*/
"dbType" : "dirty",
//the database specific settings
/*
* Database specific settings (dependent on dbType).
*
* Remember that since Etherpad 1.6.0 you can also store these informations in
* credentials.json.
*/
"dbSettings" : {
"filename" : "var/dirty.db"
},
/* An Example of MySQL Configuration
/*
* An Example of MySQL Configuration (commented out).
*
* See: https://github.com/ether/etherpad-lite/wiki/How-to-use-Etherpad-Lite-with-MySQL
*/
/*
"dbType" : "mysql",
"dbSettings" : {
"user" : "root",
"user" : "etherpaduser",
"host" : "localhost",
"password": "",
"database": "store",
"port" : 3306,
"password": "PASSWORD",
"database": "etherpad_lite_db",
"charset" : "utf8mb4"
},
*/
//the default text of a pad
/*
* The default text of a pad
*/
"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",
/* Default Pad behavior, users can override by changing */
/*
* Default Pad behavior.
*
* Change them if you want to override.
*/
"padOptions": {
"noColors": false,
"showControls": true,
@ -73,7 +114,9 @@
"lang": "en-gb"
},
/* Pad Shortcut Keys */
/*
* Pad Shortcut Keys
*/
"padShortcutEnabled" : {
"altF9" : true, /* focus on the File Menu and/or editbar */
"altC" : true, /* focus on the Chat window */
@ -99,113 +142,203 @@
"pageDown" : true
},
/* Should we suppress errors from being visible in the default Pad Text? */
/*
* Should we suppress errors from being visible in the default Pad Text?
*/
"suppressErrorsInPadText" : false,
/* Users must have a session to access pads. This effectively allows only group pads to be accessed. */
/*
* If this option is enabled, a user must have a session to access pads.
* This effectively allows only group pads to be accessed.
*/
"requireSession" : false,
/* Users may edit pads but not create new ones. Pad creation is only via the API. This applies both to group pads and regular pads. */
/*
* Users may edit pads but not create new ones.
*
* Pad creation is only via the API.
* This applies both to group pads and regular pads.
*/
"editOnly" : false,
/* Users, who have a valid session, automatically get granted access to password protected pads */
/*
* If set to true, those users who have a valid session will automatically be
* granted access to password protected pads.
*/
"sessionNoPassword" : false,
/* if true, all css & js will be minified before sending to the client. This will improve the loading performance massivly,
but makes it impossible to debug the javascript/css */
/*
* If true, all css & js will be minified before sending to the client.
*
* This will improve the loading performance massively, but makes it difficult
* to debug the javascript/css
*/
"minify" : true,
/* How long may clients use served javascript code (in seconds)? Without versioning this
may cause problems during deployment. Set to 0 to disable caching */
/*
* How long may clients use served javascript code (in seconds)?
*
* Not setting this may cause problems during deployment.
* Set to 0 to disable caching.
*/
"maxAge" : 21600, // 60 * 60 * 6 = 6 hours
/* This is the absolute path to the Abiword executable. Setting it to null, disables abiword.
Abiword is needed to advanced import/export features of pads*/
/*
* Absolute path to the Abiword executable.
*
* Abiword is needed to get advanced import/export features of pads. Setting
* it to null disables Abiword and will only allow plain text and HTML
* import/exports.
*/
"abiword" : null,
/* This is the absolute path to the soffice executable. Setting it to null, disables LibreOffice exporting.
LibreOffice can be used in lieu of Abiword to export pads */
/*
* This is the absolute path to the soffice executable.
*
* LibreOffice can be used in lieu of Abiword to export pads.
* Setting it to null disables LibreOffice exporting.
*/
"soffice" : null,
/* This is the path to the Tidy executable. Setting it to null, disables Tidy.
Tidy is used to improve the quality of exported pads*/
/*
* Path to the Tidy executable.
*
* Tidy is used to improve the quality of exported pads.
* Setting it to null disables Tidy.
*/
"tidyHtml" : null,
/* Allow import of file types other than the supported types: txt, doc, docx, rtf, odt, html & htm */
/*
* Allow import of file types other than the supported ones:
* txt, doc, docx, rtf, odt, html & htm
*/
"allowUnknownFileEnds" : true,
/* This setting is used if you require authentication of all users.
Note: /admin always requires authentication. */
/*
* This setting is used if you require authentication of all users.
*
* Note: "/admin" always requires authentication.
*/
"requireAuthentication" : false,
/* Require authorization by a module, or a user with is_admin set, see below. */
/*
* Require authorization by a module, or a user with is_admin set, see below.
*/
"requireAuthorization" : false,
/*when you use NginX or another proxy/ load-balancer set this to true*/
/*
* When you use NGINX or another proxy/load-balancer set this to true.
*/
"trustProxy" : false,
/* Privacy: disable IP logging */
/*
* Privacy: disable IP logging
*/
"disableIPlogging" : false,
/* Time (in seconds) to automatically reconnect pad when a "Force reconnect"
message is shown to user. Set to 0 to disable automatic reconnection */
"automaticReconnectionTimeout" : 0,
/*
* By default, when caret is moved out of viewport, it scrolls the minimum height needed to make this
* line visible.
* Time (in seconds) to automatically reconnect pad when a "Force reconnect"
* message is shown to user.
*
* Set to 0 to disable automatic reconnection.
*/
"automaticReconnectionTimeout" : 0,
/*
* By default, when caret is moved out of viewport, it scrolls the minimum
* height needed to make this line visible.
*/
"scrollWhenFocusLineIsOutOfViewport": {
/*
* Percentage of viewport height to be additionally scrolled.
* E.g use "percentage.editionAboveViewport": 0.5, to place caret line in the
* middle of viewport, when user edits a line above of the viewport
*
* E.g.: use "percentage.editionAboveViewport": 0.5, to place caret line in
* the middle of viewport, when user edits a line above of the
* viewport
*
* Set to 0 to disable extra scrolling
*/
"percentage": {
"editionAboveViewport": 0,
"editionBelowViewport": 0
},
/* Time (in milliseconds) used to animate the scroll transition. Set to 0 to disable animation */
"duration": 0,
/*
* Flag to control if it should scroll when user places the caret in the last line of the viewport
* Time (in milliseconds) used to animate the scroll transition.
* Set to 0 to disable animation
*/
"duration": 0,
/*
* Flag to control if it should scroll when user places the caret in the
* last line of the viewport
*/
"scrollWhenCaretIsInTheLastLineOfViewport": false,
/*
* Percentage of viewport height to be additionally scrolled when user presses arrow up
* in the line of the top of the viewport.
* Set to 0 to let the scroll to be handled as default by the Etherpad
* Percentage of viewport height to be additionally scrolled when user
* presses arrow up in the line of the top of the viewport.
*
* Set to 0 to let the scroll to be handled as default by Etherpad
*/
"percentageToScrollWhenUserPressesArrowUp": 0
},
/* Users for basic authentication. is_admin = true gives access to /admin.
If you do not uncomment this, /admin will not be available! */
/*
* Users for basic authentication.
*
* is_admin = true gives access to /admin.
* If you do not uncomment this, /admin will not be available!
*
* WARNING: passwords should not be stored in plaintext in this file.
* If you want to mitigate this, please install ep_hash_auth and
* follow the section "secure your installation" in README.md
*/
/*
"users": {
"admin": {
// "password" can be replaced with "hash" if you install ep_hash_auth
"password": "changeme1",
"is_admin": true
},
"user": {
// "password" can be replaced with "hash" if you install ep_hash_auth
"password": "changeme1",
"is_admin": false
}
},
*/
// restrict socket.io transport methods
/*
* Restrict socket.io transport methods
*/
"socketTransportProtocols" : ["xhr-polling", "jsonp-polling", "htmlfile"],
// Allow Load Testing tools to hit the Etherpad Instance. Warning this will disable security on the instance.
/*
* Allow Load Testing tools to hit the Etherpad Instance.
*
* WARNING: this will disable security on the instance.
*/
"loadTest": false,
// Disable indentation on new line when previous line ends with some special chars (':', '[', '(', '{')
/*
* Disable indentation on new line when previous line ends with some special
* chars (':', '[', '(', '{')
*/
/*
"indentationOnNewLine": false,
*/
/* The toolbar buttons configuration.
/*
* Toolbar buttons configuration.
*
* Uncomment to customize.
*/
/*
"toolbar": {
"left": [
["bold", "italic", "underline", "strikethrough"],
@ -224,30 +357,42 @@
},
*/
/* The log level we are using, can be: DEBUG, INFO, WARN, ERROR */
/*
* The log level we are using.
*
* Valid values: DEBUG, INFO, WARN, ERROR
*/
"loglevel": "INFO",
//Logging configuration. See log4js documentation for further information
// https://github.com/nomiddlename/log4js-node
// You can add as many appenders as you want here:
/*
* 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"
@ -265,7 +410,9 @@
}
}
}
}*/
}
*/
]
}
} // logconfig
}

View file

@ -5,7 +5,8 @@
"Nasir8891",
"Sankarshan",
"Aftab1995",
"Aftabuzzaman"
"Aftabuzzaman",
"আফতাবুজ্জামান"
]
},
"index.newPad": "নতুন প্যাড",

View file

@ -107,7 +107,7 @@
"timeslider.month.february": "Zemherı",
"timeslider.month.march": "Adar",
"timeslider.month.april": "Nisane",
"timeslider.month.may": "Gúlan",
"timeslider.month.may": "Gulane",
"timeslider.month.june": "Heziran",
"timeslider.month.july": "Temuz",
"timeslider.month.august": "Tebaxe",

View file

@ -1,7 +1,8 @@
{
"@metadata": {
"authors": [
"Kristian.kankainen"
"Kristian.kankainen",
"Tiblu"
]
},
"index.newPad": "Uus klade",
@ -88,7 +89,7 @@
"timeslider.toolbar.exportlink.title": "Eksport",
"timeslider.exportCurrent": "Ekspordi käesolev versioon kuiː",
"timeslider.version": "Versioon {{version}}",
"timeslider.saved": "Salvestatud {{day}}. {{month}}il {{year}}. aastal",
"timeslider.saved": "Salvestatud {{day}} {{month}} {{year}}",
"timeslider.dateformat": "{{day}}.{{month}}.{{year}} {{hours}}:{{minutes}}:{{seconds}}",
"timeslider.month.january": "Jaanuar",
"timeslider.month.february": "Veebruar",

View file

@ -120,7 +120,7 @@
"timeslider.playPause": "Lecture / Pause des contenus du pad",
"timeslider.backRevision": "Reculer dune révision dans ce pad",
"timeslider.forwardRevision": "Avancer dune révision dans ce pad",
"timeslider.dateformat": "{{day}}/{{month}}/{{year}} {{seconds}}:{{minutes}}:{{hours}}",
"timeslider.dateformat": "{{day}}/{{month}}/{{year}} {{hours}}:{{minutes}}:{{seconds}}",
"timeslider.month.january": "janvier",
"timeslider.month.february": "février",
"timeslider.month.march": "mars",

View file

@ -7,7 +7,8 @@
"Tgr",
"Csega",
"BanKris",
"Notramo"
"Notramo",
"Bencemac"
]
},
"index.newPad": "Új notesz",
@ -58,7 +59,7 @@
"pad.importExport.exportword": "Microsoft Word",
"pad.importExport.exportpdf": "PDF",
"pad.importExport.exportopen": "ODF (Open Document formátum)",
"pad.importExport.abiword.innerHTML": "Csak szöveges, vagy HTML formátumokból importálhatsz. A speciális importálási funkciókért kérjük <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\">telepítsd az abiword-öt</a>.",
"pad.importExport.abiword.innerHTML": "Csak szöveges, vagy HTML formátumokból importálhatsz. A speciális importálási funkciókért kérjük <a href=\"https://github.com/ether/etherpad-lite/wiki/How-to-enable-importing-and-exporting-different-file-formats-with-AbiWord\">telepítsd az AbiWord-öt</a>.",
"pad.modals.connected": "Kapcsolódva.",
"pad.modals.reconnecting": "Újrakapcsolódás a noteszhez...",
"pad.modals.forcereconnect": "Újrakapcsolódás kényszerítése",

View file

@ -6,7 +6,8 @@
"Hrishikesh.kb",
"Praveenp",
"Santhosh.thottingal",
"Nesi"
"Nesi",
"Jinoytommanjaly"
]
},
"index.newPad": "പുതിയ പാഡ്",
@ -61,6 +62,7 @@
"pad.modals.connected": "ബന്ധിപ്പിച്ചിരിക്കുന്നു.",
"pad.modals.reconnecting": "താങ്കളുടെ പാഡിലേയ്ക്ക് വീണ്ടും ബന്ധിപ്പിക്കുന്നു...",
"pad.modals.forcereconnect": "എന്തായാലും ബന്ധിപ്പിക്കുക",
"pad.modals.cancel": "റദ്ദാക്കുക",
"pad.modals.userdup": "മറ്റൊരു ജാലകത്തിൽ തുറന്നിരിക്കുന്നു",
"pad.modals.userdup.explanation": "ഈ കമ്പ്യൂട്ടറിൽ ഈ പാഡ് ഒന്നിലധികം ബ്രൗസർ ജാലകങ്ങളിൽ തുറന്നതായി കാണുന്നു.",
"pad.modals.userdup.advice": "ഈ ജാലകം തന്നെ ഉപയോഗിക്കാനായി ബന്ധിപ്പിക്കുക",

View file

@ -2,7 +2,8 @@
"@metadata": {
"authors": [
"Chelin",
"C.R."
"C.R.",
"Ruthven"
]
},
"index.newPad": "Nuovo Pad",
@ -15,7 +16,7 @@
"pad.toolbar.ul.title": "Ennece puntato (Ctrl+Shift+L)",
"pad.toolbar.indent.title": "Rientro (TAB)",
"pad.toolbar.unindent.title": "Riduce rientro (Shift+TAB)",
"pad.toolbar.undo.title": "Annulla (Ctrl-Z)",
"pad.toolbar.undo.title": "Sfàjere (Ctrl-Z)",
"pad.toolbar.redo.title": "Ripete (Ctrl-Y)",
"pad.toolbar.clearAuthorship.title": "Elimina 'e culure ca 'ndicanno 'e auture (Ctrl+Shift+C)",
"pad.toolbar.import_export.title": "'Mporta/esporta 'e/a diverse furmate 'e file",
@ -67,7 +68,7 @@
"pad.modals.initsocketfail.explanation": "Nun se può cunnettà 'o server e sincronizzaziona.",
"pad.modals.initsocketfail.cause": "Stu fatto è succiesso, probabbilmente pe' bbìa 'e nu probblema c' 'o navigatóre 'o ll'internet.",
"pad.modals.slowcommit.explanation": "'O server nun risponne.",
"pad.modals.slowcommit.cause": "Stu fatto può darse ca è causato pe' bbìa 'e prubbleme 'e connettività 'e rezza.",
"pad.modals.slowcommit.cause": "'Sto fatto putiss' essere causato 'a prubblemi 'e connettività 'e rezza.",
"pad.modals.badChangeset.explanation": "Nu cagnamento ca stavate facenno è stato classeficato comme illegale p' 'o server 'e sincronizzaziona.",
"pad.modals.badChangeset.cause": "Chistu fatto può darse ca è causato pe' bbìa 'e na mpustazione errata d' 'o server o cocch'atu comportamento nun preveduto. Pe' piacere cuntattate l'ammenistratore d' 'o servizio, si se pienza ca chist'è n'errore. Tentate a ve riconnettà pe' cuntinuà 'a edità.",
"pad.modals.corruptPad.explanation": "'O pad addò vulevate trasì è scassato.",

View file

@ -8,7 +8,8 @@
"Macofe",
"Pan Cube",
"Mateon1",
"Teeed"
"Teeed",
"DeRudySoulStorm"
]
},
"index.newPad": "Nowy dokument",
@ -59,7 +60,7 @@
"pad.importExport.exportword": "Microsoft Word",
"pad.importExport.exportpdf": "PDF",
"pad.importExport.exportopen": "ODF (Open Document Format)",
"pad.importExport.abiword.innerHTML": "Możesz importować pliki tylko w formacie zwykłego tekstu lub html. Aby umożliwić bardziej zaawansowane funkcje importu, <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\">zainstaluj abiword</a>.",
"pad.importExport.abiword.innerHTML": "Możesz importować pliki tylko w formacie zwykłego tekstu lub HTML. Aby umożliwić bardziej zaawansowane funkcje importu, <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\">zainstaluj AbiWord</a>.",
"pad.modals.connected": "Połączony.",
"pad.modals.reconnecting": "Ponowne łączenie z dokumentem...",
"pad.modals.forcereconnect": "Wymuś ponowne połączenie",

View file

@ -8,7 +8,8 @@
"Volkov",
"Nzeemin",
"Facenapalm",
"Patrick Star"
"Patrick Star",
"Movses"
]
},
"index.newPad": "Создать",
@ -59,7 +60,7 @@
"pad.importExport.exportword": "Microsoft Word",
"pad.importExport.exportpdf": "PDF",
"pad.importExport.exportopen": "ODF (документ OpenOffice)",
"pad.importExport.abiword.innerHTML": "Вы можете импортировать только из обычного текста или HTML. Для более продвинутых функций импорта, пожалуйста, <a href=\"https://github.com/ether/etherpad-lite/wiki/How-to-enable-importing-and-exporting-different-file-formats-with-AbiWord\">установите AbiWord</a>.",
"pad.importExport.abiword.innerHTML": "Вы можете импортировать только из обычного текста или HTML. Для более продвинутых функций импорта, пожалуйста,\n <a href=\"https://github.com/ether/etherpad-lite/wiki/How-to-enable-importing-and-exporting-different-file-formats-with-AbiWord\">установите AbiWord</a>.",
"pad.modals.connected": "Подключен.",
"pad.modals.reconnecting": "Повторное подключение к вашему документу",
"pad.modals.forcereconnect": "Принудительное переподключение",

View file

@ -4,7 +4,8 @@
"Dbc334",
"Mateju",
"Skalcaa",
"HairyFotr"
"HairyFotr",
"Upwinxp"
]
},
"index.newPad": "Nov dokument",
@ -68,7 +69,7 @@
"pad.modals.unauth.explanation": "Med pregledovanjem te strani so se dovoljenja za ogled spremenila. Poskusite se ponovno povezati.",
"pad.modals.looping.explanation": "Zaznane so težave pri komunikaciji s strežnikom za usklajevanje.",
"pad.modals.looping.cause": "Morda ste se povezali preko neustrezno nastavljenega požarnega zidu ali posredniškega strežnika.",
"pad.modals.initsocketfail": "Strežnika je nedosegljiv.",
"pad.modals.initsocketfail": "Strežnik je nedosegljiv.",
"pad.modals.initsocketfail.explanation": "Povezava s strežnikom za usklajevanje ni mogoča.",
"pad.modals.initsocketfail.cause": "Najverjetneje gre za težavo z vašim brskalnikom, ali internetno povezavo.",
"pad.modals.slowcommit.explanation": "Strežnik se ne odziva.",

View file

@ -17,11 +17,12 @@ exports.expressCreateServer = function (hook_name, args, cb) {
// Setup middleware that will package JavaScript files served by minify for
// CommonJS loader on the client-side.
// Hostname "invalid.invalid" is a dummy value to allow parsing as a URI.
var jsServer = new (Yajsml.Server)({
rootPath: 'javascripts/src/'
, rootURI: 'http://localhost:' + settings.port + '/static/js/'
, rootURI: 'http://invalid.invalid/static/js/'
, libraryPath: 'javascripts/lib/'
, libraryURI: 'http://localhost:' + settings.port + '/static/plugins/'
, libraryURI: 'http://invalid.invalid/static/plugins/'
, requestURIs: minify.requestURIs // Loop-back is causing problems, this is a workaround.
});

View file

@ -33,7 +33,7 @@ exports.basicAuth = function (req, res, next) {
var authenticate = function (cb) {
// If auth headers are present use them to authenticate...
if (req.headers.authorization && req.headers.authorization.search('Basic ') === 0) {
var userpass = new Buffer(req.headers.authorization.split(' ')[1], 'base64').toString().split(":")
var userpass = Buffer.from(req.headers.authorization.split(' ')[1], 'base64').toString().split(":")
var username = userpass.shift();
var password = userpass.join(':');
var fallback = function(success) {

View file

@ -24,6 +24,7 @@
var log4js = require('log4js')
, async = require('async')
, stats = require('./stats')
, NodeVersion = require('./utils/NodeVersion')
;
log4js.replaceConsole();
@ -39,6 +40,16 @@ var settings
var npm = require("npm/lib/npm.js");
async.waterfall([
function(callback)
{
NodeVersion.enforceMinNodeVersion('6.9.0', callback);
},
function(callback)
{
NodeVersion.checkDeprecationStatus('8.9.0', '1.8.0', callback);
},
// load npm
function(callback) {
npm.load({}, function(er) {

View file

@ -1,3 +1,19 @@
var measured = require('measured')
/*
* TODO: this polyfill is needed for Node 6.9 support.
*
* Once minimum supported Node version is raised to 8.9.0, it will be removed.
*/
if (!Object.values) {
var log4js = require('log4js');
var statsLogger = log4js.getLogger("stats");
statsLogger.warn(`Enabling a polyfill to run on this Node version (${process.version}). Next Etherpad version will remove support for Node version < 8.9.0. Please update your runtime.`);
var values = require('object.values');
values.shim();
}
var measured = require('measured-core')
module.exports = measured.createCollection();

View file

@ -40,12 +40,12 @@ for ( var i = 0; i < argv.length; i++ ) {
}
// Override location of settings.json file
if ( prevArg == '--sessionkey' || prevArg == '-k' ) {
if ( prevArg == '--sessionkey' ) {
exports.argv.sessionkey = arg;
}
// Override location of settings.json file
if ( prevArg == '--apikey' || prevArg == '-k' ) {
if ( prevArg == '--apikey' ) {
exports.argv.apikey = arg;
}

View file

@ -21,10 +21,11 @@
var Changeset = require("ep_etherpad-lite/static/js/Changeset");
exports.getPadPlainText = function(pad, revNum){
var atext = ((revNum !== undefined) ? pad.getInternalRevisionAText(revNum) : pad.atext());
var _analyzeLine = exports._analyzeLine;
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 apool = pad.pool;
var pieces = [];
for (var i = 0; i < textLines.length; i++){

View file

@ -268,7 +268,7 @@ function getHTMLFromAtext(pad, atext, authorColors)
}
// close all tags upto the outer most
if (outermostTag != -1)
if (outermostTag !== -1)
{
while ( outermostTag >= 0 )
{
@ -282,7 +282,7 @@ function getHTMLFromAtext(pad, atext, authorColors)
{
if (openTags.indexOf(usedAttribs[i]) === -1)
{
emitOpenTag(usedAttribs[i])
emitOpenTag(usedAttribs[i]);
}
}
@ -304,7 +304,7 @@ function getHTMLFromAtext(pad, atext, authorColors)
// close all the tags that are open after the last op
while (openTags.length > 0)
{
emitCloseTag(openTags[0])
emitCloseTag(openTags[0]);
}
} // end processNextChars
if (urls)
@ -333,146 +333,126 @@ function getHTMLFromAtext(pad, atext, authorColors)
// 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'], ...]
var listLevels = []
var openLists = [];
for (var i = 0; i < textLines.length; i++)
{
var context;
var line = _analyzeLine(textLines[i], attribLines[i], apool);
var lineContent = getLineHTML(line.text, line.aline);
listLevels.push(line.listLevel)
if (line.listLevel)//If we are inside a list
{
// do list stuff
var whichList = -1; // index into lists or -1
if (line.listLevel)
{
whichList = lists.length;
for (var j = lists.length - 1; j >= 0; j--)
{
if (line.listLevel <= lists[j][0])
{
whichList = j;
}
}
}
if (whichList >= lists.length)//means we are on a deeper level of indentation than the previous line
{
if(lists.length > 0){
pieces.push('</li>')
}
lists.push([line.listLevel, line.listTypeName]);
// if there is a previous list we need to open x tags, where x is the difference of the levels
// if there is no previous list we need to open x tags, where x is the wanted level
var toOpen = lists.length > 1 ? line.listLevel - lists[lists.length - 2][0] - 1 : line.listLevel - 1
if(line.listTypeName == "number")
{
if(toOpen > 0){
pieces.push(new Array(toOpen + 1).join('<ol>'))
}
pieces.push('<ol class="'+line.listTypeName+'"><li>', lineContent || '<br>');
}
else
{
if(toOpen > 0){
pieces.push(new Array(toOpen + 1).join('<ul>'))
}
pieces.push('<ul class="'+line.listTypeName+'"><li>', lineContent || '<br>');
}
}
//the following code *seems* dead after my patch.
//I keep it just in case I'm wrong...
/*else if (whichList == -1)//means we are not inside a list
{
if (line.text)
{
console.log('trace 1');
// non-blank line, end all lists
if(line.listTypeName == "number")
{
pieces.push(new Array(lists.length + 1).join('</li></ol>'));
}
else
{
pieces.push(new Array(lists.length + 1).join('</li></ul>'));
}
lists.length = 0;
pieces.push(lineContent, '<br>');
}
else
{
console.log('trace 2');
pieces.push('<br><br>');
}
}*/
else//means we are getting closer to the lowest level of indentation or are at the same level
{
var toClose = lists.length > 0 ? listLevels[listLevels.length - 2] - line.listLevel : 0
if( toClose > 0){
pieces.push('</li>')
if(lists[lists.length - 1][1] == "number")
{
pieces.push(new Array(toClose+1).join('</ol>'))
pieces.push('<li>', lineContent || '<br>');
}
else
{
pieces.push(new Array(toClose+1).join('</ul>'))
pieces.push('<li>', lineContent || '<br>');
}
lists = lists.slice(0,whichList+1)
} else {
pieces.push('</li><li>', lineContent || '<br>');
}
}
}
else//outside any list, need to close line.listLevel of lists
{
if(lists.length > 0){
if(lists[lists.length - 1][1] == "number"){
pieces.push('</li></ol>');
pieces.push(new Array(listLevels[listLevels.length - 2]).join('</ol>'))
} else {
pieces.push('</li></ul>');
pieces.push(new Array(listLevels[listLevels.length - 2]).join('</ul>'))
}
}
lists = []
var context = {
context = {
line: line,
lineContent: lineContent,
apool: apool,
attribLine: attribLines[i],
text: textLines[i],
padId: pad.id
};
var prevLine = null;
var nextLine = null;
if (i > 0)
{
prevLine = _analyzeLine(textLines[i -1], attribLines[i -1], apool);
}
if (i < textLines.length)
{
nextLine = _analyzeLine(textLines[i + 1], attribLines[i + 1], apool);
}
hooks.aCallAll('getLineHTMLForExport', context);
//To create list parent elements
if ((!prevLine || prevLine.listLevel !== line.listLevel) || (prevLine && line.listTypeName !== prevLine.listTypeName))
{
var exists = _.find(openLists, function (item)
{
return (item.level === line.listLevel && item.type === line.listTypeName);
});
if (!exists) {
var prevLevel = 0;
if (prevLine && prevLine.listLevel) {
prevLevel = prevLine.listLevel;
}
if (prevLine && line.listTypeName !== prevLine.listTypeName)
{
prevLevel = 0;
}
var lineContentFromHook = hooks.callAllStr("getLineHTMLForExport", context, " ", " ", "");
for (var diff = prevLevel; diff < line.listLevel; diff++) {
openLists.push({level: diff, type: line.listTypeName});
var prevPiece = pieces[pieces.length - 1];
if (lineContentFromHook)
if (prevPiece.indexOf("<ul") === 0 || prevPiece.indexOf("<ol") === 0 || prevPiece.indexOf("</li>") === 0)
{
pieces.push(lineContentFromHook, '');
pieces.push("<li>");
}
if (line.listTypeName === "number")
{
pieces.push("<ol class=\"" + line.listTypeName + "\">");
}
else
{
pieces.push(lineContent, '<br>');
pieces.push("<ul class=\"" + line.listTypeName + "\">");
}
}
}
}
for (var k = lists.length - 1; k >= 0; k--)
pieces.push("<li>", context.lineContent);
// To close list elements
if (nextLine && nextLine.listLevel === line.listLevel && line.listTypeName === nextLine.listTypeName)
{
if(lists[k][1] == "number")
pieces.push("</li>");
}
if ((!nextLine || !nextLine.listLevel || nextLine.listLevel < line.listLevel) || (nextLine && line.listTypeName !== nextLine.listTypeName))
{
pieces.push('</li></ol>');
var nextLevel = 0;
if (nextLine && nextLine.listLevel) {
nextLevel = nextLine.listLevel;
}
if (nextLine && line.listTypeName !== nextLine.listTypeName)
{
nextLevel = 0;
}
for (var diff = nextLevel; diff < line.listLevel; diff++)
{
openLists = openLists.filter(function(el)
{
return el.level !== diff && el.type !== line.listTypeName;
});
if (pieces[pieces.length - 1].indexOf("</ul") === 0 || pieces[pieces.length - 1].indexOf("</ol") === 0)
{
pieces.push("</li>");
}
if (line.listTypeName === "number")
{
pieces.push("</ol>");
}
else
{
pieces.push('</li></ul>');
pieces.push("</ul>");
}
}
}
}
else//outside any list, need to close line.listLevel of lists
{
context = {
line: line,
lineContent: lineContent,
apool: apool,
attribLine: attribLines[i],
text: textLines[i],
padId: pad.id
};
hooks.aCallAll("getLineHTMLForExport", context);
pieces.push(context.lineContent, "<br>");
}
}

View file

@ -264,7 +264,8 @@ function getAceFile(callback) {
async.forEach(founds, function (item, callback) {
var filename = item.match(/"([^"]*)"/)[1];
var baseURI = 'http://localhost:' + settings.port;
// Hostname "invalid.invalid" is a dummy value to allow parsing as a URI.
var baseURI = 'http://invalid.invalid';
var resourceURI = baseURI + path.normalize(path.join('/static/', filename));
resourceURI = resourceURI.replace(/\\/g, '/'); // Windows (safe generally?)

View file

@ -0,0 +1,54 @@
/**
* Checks related to Node runtime version
*/
/*
* 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.
*/
/**
* Quits if Etherpad is not running on a given minimum Node version
*
* @param {String} minNodeVersion Minimum required Node version
* @param {Function} callback Standard callback function
*/
exports.enforceMinNodeVersion = function(minNodeVersion, callback) {
const semver = require('semver');
const currentNodeVersion = process.version;
// we cannot use template literals, since we still do not know if we are
// running under Node >= 4.0
if (semver.lt(currentNodeVersion, minNodeVersion)) {
console.error('Running Etherpad on Node ' + currentNodeVersion + ' is not supported. Please upgrade at least to Node ' + minNodeVersion);
} else {
console.debug('Running on Node ' + currentNodeVersion + ' (minimum required Node version: ' + minNodeVersion + ')');
callback();
}
};
/**
* Prints a warning if running on a supported but deprecated Node version
*
* @param {String} lowestNonDeprecatedNodeVersion all Node version less than this one are deprecated
* @param {Function} epRemovalVersion Etherpad version that will remove support for deprecated Node releases
*/
exports.checkDeprecationStatus = function(lowestNonDeprecatedNodeVersion, epRemovalVersion, callback) {
const semver = require('semver');
const currentNodeVersion = process.version;
if (semver.lt(currentNodeVersion, lowestNonDeprecatedNodeVersion)) {
console.warn(`Support for Node ${currentNodeVersion} will be removed in Etherpad ${epRemovalVersion}. Please consider updating at least to Node ${lowestNonDeprecatedNodeVersion}`);
}
callback();
};

View file

@ -49,7 +49,7 @@ CachingMiddleware.prototype = new function () {
(req.get('Accept-Encoding') || '').indexOf('gzip') != -1;
var path = require('url').parse(req.url).path;
var cacheKey = (new Buffer(path)).toString('base64').replace(/[\/\+=]/g, '');
var cacheKey = Buffer.from(path).toString('base64').replace(/[/+=]/g, '');
fs.stat(CACHE_DIR + 'minified_' + cacheKey, function (error, stats) {
var modifiedSince = (req.headers['if-modified-since']

View file

@ -2,59 +2,83 @@
"name": "ep_etherpad-lite",
"description": "A Etherpad based on node.js",
"homepage": "http://etherpad.org",
"keywords" : ["etherpad", "realtime", "collaborative", "editor"],
"keywords": [
"etherpad",
"realtime",
"collaborative",
"editor"
],
"author": "Etherpad Foundation",
"contributors": [
{ "name": "John McLear" },
{ "name": "Hans Pinckaers" },
{ "name": "Robin Buse" },
{ "name": "Marcel Klehr" },
{ "name": "Peter Martischka" }
{
"name": "John McLear"
},
{
"name": "Hans Pinckaers"
},
{
"name": "Robin Buse"
},
{
"name": "Marcel Klehr"
},
{
"name": "Peter Martischka"
}
],
"dependencies": {
"etherpad-yajsml" : "0.0.2",
"request" : "2.83.0",
"etherpad-require-kernel" : "1.0.9",
"resolve" : "1.1.7",
"socket.io" : "1.7.3",
"ueberdb2" : "0.3.8",
"express" : "4.13.4",
"express-session" : "1.13.0",
"cookie-parser" : "1.3.4",
"async": "0.9.0",
"clean-css" : "3.4.19",
"uglify-js" : "2.6.2",
"formidable" : "1.2.1",
"log4js" : "0.6.35",
"cheerio" : "0.20.0",
"async-stacktrace": "0.0.2",
"npm" : ">=4.0.2",
"ejs" : "2.5.7",
"graceful-fs" : "4.1.3",
"slide" : "1.1.6",
"semver" : "5.1.0",
"security" : "1.0.0",
"tinycon" : "0.0.1",
"underscore" : "1.8.3",
"unorm" : "1.4.1",
"languages4translatewiki" : "0.1.3",
"swagger-node-express" : "2.1.3",
"channels": "0.0.4",
"cheerio": "0.20.0",
"clean-css": "3.4.19",
"cookie-parser": "1.3.4",
"ejs": "2.5.7",
"etherpad-require-kernel": "1.0.9",
"etherpad-yajsml": "0.0.2",
"express": "4.16.3",
"express-session": "1.15.6",
"formidable": "1.2.1",
"graceful-fs": "4.1.3",
"jsonminify": "0.4.1",
"measured" : "1.1.0",
"mocha" : "5.0.5",
"supertest" : "3.0.0"
"languages4translatewiki": "0.1.3",
"log4js": "0.6.35",
"measured-core": "1.11.2",
"npm": "6.4.0",
"object.values": "^1.0.4",
"request": "2.83.0",
"resolve": "1.1.7",
"security": "1.0.0",
"semver": "5.1.0",
"slide": "1.1.6",
"socket.io": "1.7.3",
"swagger-node-express": "2.1.3",
"tinycon": "0.0.1",
"ueberdb2": "0.4.0",
"uglify-js": "2.6.2",
"underscore": "1.8.3",
"unorm": "1.4.1"
},
"bin": {
"etherpad-lite": "./node/server.js"
},
"bin": { "etherpad-lite": "./node/server.js" },
"devDependencies": {
"wd" : "1.6.1"
"mocha": "5.2.0",
"nyc": "^12.0.2",
"supertest": "3.0.0",
"wd": "1.10.3"
},
"engines" : { "node" : ">=0.10.0",
"npm" : ">=1.0"
"engines": {
"node": ">=6.9.0",
"npm": ">=3.10.8"
},
"repository" : { "type" : "git",
"repository": {
"type": "git",
"url": "http://github.com/ether/etherpad-lite.git"
},
"version" : "1.6.6",
"scripts": {
"test": "nyc mocha --timeout 5000 ../tests/backend/specs/api"
},
"version": "1.7.0",
"license": "Apache-2.0"
}

View file

@ -4,6 +4,10 @@ var _ = require('./underscore');
var lineMarkerAttribute = 'lmkr';
// Some of these attributes are kept for compatibility purposes.
// Not sure if we need all of them
var DEFAULT_LINE_ATTRIBUTES = ['author', 'lmkr', 'insertorder', 'start'];
// If one of these attributes are set to the first character of a
// line it is considered as a line attribute marker i.e. attributes
// set on this marker are applied to the whole line.
@ -35,6 +39,7 @@ var AttributeManager = function(rep, applyChangesetCallback)
// it will be considered as a line marker
};
AttributeManager.DEFAULT_LINE_ATTRIBUTES = DEFAULT_LINE_ATTRIBUTES;
AttributeManager.lineAttributes = lineAttributes;
AttributeManager.prototype = _(AttributeManager.prototype).extend({
@ -375,7 +380,7 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({
ChangesetUtils.buildKeepToStartOfRange(this.rep, builder, [lineNum, 0]);
var countAttribsWithMarker = _.chain(attribs).filter(function(a){return !!a[1];})
.map(function(a){return a[0];}).difference(['author', 'lmkr', 'insertorder', 'start']).size().value();
.map(function(a){return a[0];}).difference(DEFAULT_LINE_ATTRIBUTES).size().value();
//if we have marker and any of attributes don't need to have marker. we need delete it
if(hasMarker && !countAttribsWithMarker){

View file

@ -1778,19 +1778,15 @@ function Ace2Inner(){
strikethrough: true,
list: true
};
var OTHER_INCORPED_ATTRIBS = {
insertorder: true,
author: true
};
function isStyleAttribute(aname)
{
return !!STYLE_ATTRIBS[aname];
}
function isOtherIncorpedAttribute(aname)
function isDefaultLineAttribute(aname)
{
return !!OTHER_INCORPED_ATTRIBS[aname];
return AttributeManager.DEFAULT_LINE_ATTRIBUTES.indexOf(aname) !== -1;
}
function insertDomLines(nodeToAddAfter, infoStructs, isTimeUp)
@ -2757,9 +2753,12 @@ function Ace2Inner(){
function analyzeChange(oldText, newText, oldAttribs, newAttribs, optSelStartHint, optSelEndHint)
{
// we need to take into account both the styles attributes & attributes defined by
// the plugins, so basically we can ignore only the default line attribs used by
// Etherpad
function incorpedAttribFilter(anum)
{
return !isOtherIncorpedAttribute(rep.apool.getAttribKey(anum));
return !isDefaultLineAttribute(rep.apool.getAttribKey(anum));
}
function attribRuns(attribs)
@ -3708,8 +3707,8 @@ function Ace2Inner(){
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 || browser.chrome) ? (type == "keydown") : (type == "keypress"));
var isTypeForCmdKey = ((browser.msie || browser.safari || browser.chrome) ? (type == "keydown") : (type == "keypress"));
var isTypeForSpecialKey = ((browser.msie || browser.safari || browser.chrome || browser.firefox) ? (type == "keydown") : (type == "keypress"));
var isTypeForCmdKey = ((browser.msie || browser.safari || browser.chrome || browser.firefox) ? (type == "keydown") : (type == "keypress"));
var stopped = false;
inCallStackIfNecessary("handleKeyEvent", function()

View file

@ -214,7 +214,7 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument)
result.clearSpans = function()
{
html = [];
lineClass = ''; // non-null to cause update
lineClass = 'ace-line';
result.lineMarker = 0;
};

View file

@ -27,7 +27,7 @@
<p>Git sha: <a href='https://github.com/ether/etherpad-lite/commit/<%= gitCommit %>'><%= gitCommit %></a></p>
<h2>Installed plugins</h2>
<pre><%- plugins.formatPlugins().replace(", ","\n") %></pre>
<pre><%- plugins.formatPlugins().replace(/, /g,"\n") %></pre>
<h2>Installed parts</h2>
<pre><%= plugins.formatParts() %></pre>

View file

@ -20,7 +20,7 @@ ol {
padding-left: 0;
}
body > ol {
counter-reset: first second third fourth fifth sixth seventh eigth ninth tenth eleventh twelth thirteenth fourteenth fifteenth sixteenth;
counter-reset: first second third fourth fifth sixth seventh eighth ninth tenth eleventh twelfth thirteenth fourteenth fifteenth sixteenth;
}
ol > li:before {
content: counter(first) ". ";
@ -51,40 +51,40 @@ ol > ol > ol > ol > ol > ol > ol > li:before {
counter-increment: seventh;
}
ol > ol > ol > ol > ol > ol > ol > ol > li:before {
content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(seventh) "." counter(eigth) ". ";
counter-increment: eigth;
content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(seventh) "." counter(eighth) ". ";
counter-increment: eighth;
}
ol > ol > ol > ol > ol > ol > ol > ol > ol > li:before {
content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(seventh) "." counter(eigth) "." counter(ninth) ". ";
content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(seventh) "." counter(eighth) "." counter(ninth) ". ";
counter-increment: ninth;
}
ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > li:before {
content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(seventh) "." counter(eigth) "." counter(ninth) "." counter(tenth) ". ";
content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(seventh) "." counter(eighth) "." counter(ninth) "." counter(tenth) ". ";
counter-increment: tenth;
}
ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > li:before {
content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(seventh) "." counter(eigth) "." counter(ninth) "." counter(tenth) "." counter(eleventh) ". ";
content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(seventh) "." counter(eighth) "." counter(ninth) "." counter(tenth) "." counter(eleventh) ". ";
counter-increment: eleventh;
}
ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > li:before {
content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(seventh) "." counter(eigth) "." counter(ninth) "." counter(tenth) "." counter(eleventh) "." counter(twelth) ". ";
counter-increment: twelth;
content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(seventh) "." counter(eighth) "." counter(ninth) "." counter(tenth) "." counter(eleventh) "." counter(twelfth) ". ";
counter-increment: twelfth;
}
ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > li:before {
content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(seventh) "." counter(eigth) "." counter(ninth) "." counter(tenth) "." counter(eleventh) "." counter(twelth) "." counter(thirteenth) ". ";
content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(seventh) "." counter(eighth) "." counter(ninth) "." counter(tenth) "." counter(eleventh) "." counter(twelfth) "." counter(thirteenth) ". ";
counter-increment: thirteenth;
}
ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > li:before {
content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(seventh) "." counter(eigth) "." counter(ninth) "." counter(tenth) "." counter(eleventh) "." counter(twelth) "." counter(thirteenth) "." counter(fourteenth) ". ";
content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(seventh) "." counter(eighth) "." counter(ninth) "." counter(tenth) "." counter(eleventh) "." counter(twelfth) "." counter(thirteenth) "." counter(fourteenth) ". ";
counter-increment: fourteenth;
}
ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > li:before {
content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(seventh) "." counter(eigth) "." counter(ninth) "." counter(tenth) "." counter(eleventh) "." counter(twelth) "." counter(thirteenth) "." counter(fourteenth) "." counter(fifteenth) ". ";
content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(seventh) "." counter(eighth) "." counter(ninth) "." counter(tenth) "." counter(eleventh) "." counter(twelfth) "." counter(thirteenth) "." counter(fourteenth) "." counter(fifteenth) ". ";
counter-increment: fifteenth;
}
ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > li:before {
content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(seventh) "." counter(eigth) "." counter(ninth) "." counter(tenth) "." counter(eleventh) "." counter(twelth) "." counter(thirteenth) "." counter(fourteenth) "." counter(fifteenth) "." counter(sixthteenth) ". ";
counter-increment: sixthteenth;
content: counter(first) "." counter(second) "." counter(third) "." counter(fourth) "." counter(fifth) "." counter(sixth) "." counter(seventh) "." counter(eighth) "." counter(ninth) "." counter(tenth) "." counter(eleventh) "." counter(twelfth) "." counter(thirteenth) "." counter(fourteenth) "." counter(fifteenth) "." counter(sixteenth) ". ";
counter-increment: sixteenth;
}
ol {
text-indent: 0px;

View file

@ -1,9 +1,11 @@
# About this folder: Tests
Before running the tests, start an Etherpad instance on your machine.
## Frontend
To run the tests, point your browser to `<yourdomainhere>/tests/frontend`
To run the frontend tests, point your browser to `<yourdomainhere>/tests/frontend`
## Backend
To run the tests, run ``bin/backendTests.sh``
To run the backend tests, run `cd src` and then `npm test`

View file

@ -14,7 +14,19 @@ var apiVersion = 1;
var testPadId = makeid();
var lastEdited = "";
var text = generateLongText();
var ULhtml = '<!DOCTYPE html><html><body><ul class="bullet"><li>one</li><li>2</li></ul><br><ul><ul class="bullet"><li>UL2</li></ul></ul></body></html>';
/*
* Html document with nested lists of different types, to test its import and
* verify it is exported back correctly
*/
var ulHtml = '<!doctype html><html><body><ul class="bullet"><li>one</li><li>two</li><li>0</li><li>1</li><li>2<ul class="bullet"><li>3</li><li>4</li></ul></li></ul><ol class="number"><li>item<ol class="number"><li>item1</li><li>item2</li></ol></li></ol></body></html>';
/*
* When exported back, Etherpad produces an html which is not exactly the same
* textually, but at least it remains standard compliant and has an equal DOM
* structure.
*/
var expectedHtml = '<!doctype html><html><body><ul class="bullet"><li>one</li><li>two</li><li>0</li><li>1</li><li>2<ul class="bullet"><li>3</li><li>4</ul></li></ul><ol class="number"><li>item<ol class="number"><li>item1</li><li>item2</ol></li></ol></body></html>';
describe('Connectivity', function(){
it('errors if can not connect', function(done) {
@ -522,8 +534,8 @@ describe('setHTML', function(){
})
describe('setHTML', function(){
it('Sets the HTML of a Pad with a bunch of weird unordered lists inserted', function(done) {
api.get(endPoint('setHTML')+"&padID="+testPadId+"&html="+ULhtml)
it('Sets the HTML of a Pad with complex nested lists of different types', function(done) {
api.get(endPoint('setHTML')+"&padID="+testPadId+"&html="+ulHtml)
.expect(function(res){
if(res.body.code !== 0) throw new Error("List HTML cant be imported")
})
@ -533,12 +545,22 @@ describe('setHTML', function(){
})
describe('getHTML', function(){
it('Gets the HTML of a Pad with a bunch of weird unordered lists inserted', function(done) {
it('Gets back the HTML of a Pad with complex nested lists of different types', function(done) {
api.get(endPoint('getHTML')+"&padID="+testPadId)
.expect(function(res){
var ehtml = res.body.data.html.replace("<br></body>", "</body>").toLowerCase();
var uhtml = ULhtml.toLowerCase();
if(ehtml !== uhtml) throw new Error("Imported HTML does not match served HTML")
var receivedHtml = res.body.data.html.replace("<br></body>", "</body>").toLowerCase();
if (receivedHtml !== expectedHtml) {
throw new Error(`HTML received from export is not the one we were expecting.
Received:
${receivedHtml}
Expected:
${expectedHtml}
Which is a slightly modified version of the originally imported one:
${ulHtml}`);
}
})
.expect('Content-Type', /json/)
.expect(200, done)

View file

@ -0,0 +1,105 @@
describe('author of pad edition', function() {
var REGULAR_LINE = 0;
var LINE_WITH_ORDERED_LIST = 1;
var LINE_WITH_UNORDERED_LIST = 2;
// author 1 creates a new pad with some content (regular lines and lists)
before(function(done) {
var padId = helper.newPad(function() {
// make sure pad has at least 3 lines
var $firstLine = helper.padInner$('div').first();
var threeLines = ['regular line', 'line with ordered list', 'line with unordered list'].join('<br>');
$firstLine.html(threeLines);
// wait for lines to be processed by Etherpad
helper.waitFor(function() {
var $lineWithUnorderedList = getLine(LINE_WITH_UNORDERED_LIST);
return $lineWithUnorderedList.text() === 'line with unordered list';
}).done(function() {
// create the unordered list
var $lineWithUnorderedList = getLine(LINE_WITH_UNORDERED_LIST);
$lineWithUnorderedList.sendkeys('{selectall}');
var $insertUnorderedListButton = helper.padChrome$('.buttonicon-insertunorderedlist');
$insertUnorderedListButton.click();
helper.waitFor(function() {
var $lineWithUnorderedList = getLine(LINE_WITH_UNORDERED_LIST);
return $lineWithUnorderedList.find('ul li').length === 1;
}).done(function() {
// create the ordered list
var $lineWithOrderedList = getLine(LINE_WITH_ORDERED_LIST);
$lineWithOrderedList.sendkeys('{selectall}');
var $insertOrderedListButton = helper.padChrome$('.buttonicon-insertorderedlist');
$insertOrderedListButton.click();
helper.waitFor(function() {
var $lineWithOrderedList = getLine(LINE_WITH_ORDERED_LIST);
return $lineWithOrderedList.find('ol li').length === 1;
}).done(function() {
// Reload pad, to make changes as a second user. Need a timeout here to make sure
// all changes were saved before reloading
setTimeout(function() {
helper.newPad(done, padId);
}, 1000);
});
});
});
});
this.timeout(60000);
});
// author 2 makes some changes on the pad
it('marks only the new content as changes of the second user on a regular line', function(done) {
changeLineAndCheckOnlyThatChangeIsFromThisAuthor(REGULAR_LINE, 'x', done);
});
it('marks only the new content as changes of the second user on a line with ordered list', function(done) {
changeLineAndCheckOnlyThatChangeIsFromThisAuthor(LINE_WITH_ORDERED_LIST, 'y', done);
});
it('marks only the new content as changes of the second user on a line with unordered list', function(done) {
changeLineAndCheckOnlyThatChangeIsFromThisAuthor(LINE_WITH_UNORDERED_LIST, 'z', done);
});
/* ********************** Helper functions ************************ */
var getLine = function(lineNumber) {
return helper.padInner$('div').eq(lineNumber);
}
var getAuthorFromClassList = function(classes) {
return classes.find(function(cls) {
return cls.startsWith('author');
});
}
var changeLineAndCheckOnlyThatChangeIsFromThisAuthor = function(lineNumber, textChange, done) {
// get original author class
var classes = getLine(lineNumber).find('span').first().attr('class').split(' ');
var originalAuthor = getAuthorFromClassList(classes);
// make change on target line
var $regularLine = getLine(lineNumber);
helper.selectLines($regularLine, $regularLine, 2, 2); // place caret after 2nd char of line
$regularLine.sendkeys(textChange);
// wait for change to be processed by Etherpad
var otherAuthorsOfLine;
helper.waitFor(function() {
var authorsOfLine = getLine(lineNumber).find('span').map(function() {
return getAuthorFromClassList($(this).attr('class').split(' '));
}).get();
otherAuthorsOfLine = authorsOfLine.filter(function(author) {
return author !== originalAuthor;
});
var lineHasChangeOfThisAuthor = otherAuthorsOfLine.length > 0;
return lineHasChangeOfThisAuthor;
}).done(function() {
var thisAuthor = otherAuthorsOfLine[0];
var $changeOfThisAuthor = getLine(lineNumber).find('span.' + thisAuthor);
expect($changeOfThisAuthor.text()).to.be(textChange);
done();
});
}
});

View file

@ -44,7 +44,7 @@ describe("bold button", function(){
//select this text element
$firstTextElement.sendkeys('{selectall}');
if(inner$(window)[0].bowser.firefox || inner$(window)[0].bowser.modernIE){ // if it's a mozilla or IE
if(inner$(window)[0].bowser.modernIE){ // if it's IE
var evtType = "keypress";
}else{
var evtType = "keydown";

View file

@ -15,7 +15,7 @@ describe("indentation button", function(){
//select this text element
$firstTextElement.sendkeys('{selectall}');
if(inner$(window)[0].bowser.firefox || inner$(window)[0].bowser.modernIE){ // if it's a mozilla or IE
if(inner$(window)[0].bowser.modernIE){ // if it's IE
var evtType = "keypress";
}else{
var evtType = "keydown";
@ -325,7 +325,7 @@ describe("indentation button", function(){
function pressEnter(){
var inner$ = helper.padInner$;
if(inner$(window)[0].bowser.firefox || inner$(window)[0].bowser.modernIE){ // if it's a mozilla or IE
if(inner$(window)[0].bowser.modernIE){ // if it's IE
var evtType = "keypress";
}else{
var evtType = "keydown";

View file

@ -44,7 +44,7 @@ describe("italic some text", function(){
//select this text element
$firstTextElement.sendkeys('{selectall}');
if(inner$(window)[0].bowser.firefox || inner$(window)[0].bowser.modernIE){ // if it's a mozilla or IE
if(inner$(window)[0].bowser.modernIE){ // if it's IE
var evtType = "keypress";
}else{
var evtType = "keydown";

View file

@ -111,7 +111,7 @@ describe("assign ordered list", function(){
var triggerCtrlShiftShortcut = function(shortcutChar) {
var inner$ = helper.padInner$;
if(inner$(window)[0].bowser.firefox || inner$(window)[0].bowser.modernIE) { // if it's a mozilla or IE
if(inner$(window)[0].bowser.modernIE) { // if it's IE
var evtType = "keypress";
}else{
var evtType = "keydown";

View file

@ -513,7 +513,7 @@ describe('scroll when focus line is out of viewport', function () {
var pressKey = function(keyCode, shiftIsPressed){
var inner$ = helper.padInner$;
var evtType;
if(inner$(window)[0].bowser.firefox || inner$(window)[0].bowser.modernIE){ // if it's a mozilla or IE
if(inner$(window)[0].bowser.modernIE){ // if it's IE
evtType = 'keypress';
}else{
evtType = 'keydown';

View file

@ -88,7 +88,7 @@ describe("select formatting buttons when selection has style applied", function(
//select this text element
$firstTextElement.sendkeys('{selectall}');
if(inner$(window)[0].bowser.firefox || inner$(window)[0].bowser.modernIE){ // if it's a mozilla or IE
if(inner$(window)[0].bowser.modernIE){ // if it's IE
var evtType = "keypress";
}else{
var evtType = "keydown";

View file

@ -44,11 +44,11 @@ describe("undo button", function(){
var modifiedValue = $firstTextElement.text(); // get the modified value
expect(modifiedValue).not.to.be(originalValue); // expect the value to change
if(inner$(window)[0].bowser.firefox){ // if it's a mozilla browser
var evtType = "keypress";
}else{
/*
* ACHTUNG: this is the only place in the test codebase in which a keydown
* is sent for IE. Everywhere else IE uses keypress.
*/
var evtType = "keydown";
}
var e = inner$.Event(evtType);
e.ctrlKey = true; // Control key

View file

@ -12,14 +12,13 @@ var config = {
var allTestsPassed = true;
var sauceTestWorker = async.queue(function (testSettings, callback) {
var browser = wd.remote(config.host, config.port, config.username, config.accessKey);
var browserChain = browser.chain();
var browser = wd.promiseChainRemote(config.host, config.port, config.username, config.accessKey);
var name = process.env.GIT_HASH + " - " + testSettings.browserName + " " + testSettings.version + ", " + testSettings.platform;
testSettings.name = name;
testSettings["public"] = true;
testSettings["build"] = process.env.GIT_HASH;
browserChain.init(testSettings).get("http://localhost:9001/tests/frontend/", function(){
browser.init(testSettings).get("http://localhost:9001/tests/frontend/", function(){
var url = "https://saucelabs.com/jobs/" + browser.sessionID;
console.log("Remote sauce test '" + name + "' started! " + url);
@ -28,7 +27,7 @@ var sauceTestWorker = async.queue(function (testSettings, callback) {
getStatusInterval && clearInterval(getStatusInterval);
clearTimeout(timeout);
browserChain.quit();
browser.quit();
if(!success){
allTestsPassed = false;
@ -53,7 +52,7 @@ var sauceTestWorker = async.queue(function (testSettings, callback) {
var knownConsoleText = "";
var getStatusInterval = setInterval(function(){
browserChain.eval("$('#console').text()", function(err, consoleText){
browser.eval("$('#console').text()", function(err, consoleText){
if(!consoleText || err){
return;
}