mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-01-19 22:23:33 +01:00
Merge pull request #1 from ether/develop
Update to most recent etherpad-lite version
This commit is contained in:
commit
f2836125dc
32 changed files with 574 additions and 171 deletions
|
@ -4,7 +4,7 @@
|
|||
# About
|
||||
Etherpad is a really-real time collaborative editor maintained by the Etherpad Community.
|
||||
|
||||
Etherpad is written in JavaScript(99.9%) on both the server and client so it's easy for developers to maintain and add new features. Because of this Etherpad has tons of customizations that you can leverage.
|
||||
Etherpad is written in JavaScript (99.9%) on both the server and client so it's easy for developers to maintain and add new features. Because of this Etherpad has tons of customizations that you can leverage.
|
||||
|
||||
Etherpad is designed to be easily embeddable and provides a [HTTP API](https://github.com/ether/etherpad-lite/wiki/HTTP-API)
|
||||
that allows your web application to manage pads, users and groups. It is recommended to use the [available client implementations](https://github.com/ether/etherpad-lite/wiki/HTTP-API-client-libraries) in order to interact with this API.
|
||||
|
|
|
@ -46,13 +46,14 @@ fi
|
|||
#check node version
|
||||
NODE_VERSION=$(node --version)
|
||||
NODE_V_MINOR=$(echo $NODE_VERSION | cut -d "." -f 1-2)
|
||||
NODE_V_MAIN=$(echo $NODE_VERSION | cut -d "." -f 1)
|
||||
#iojs version checking added
|
||||
if hash iojs 2>/dev/null; then
|
||||
IOJS_VERSION=$(iojs --version)
|
||||
fi
|
||||
if [ ! $NODE_V_MINOR = "v0.10" ] && [ ! $NODE_V_MINOR = "v0.11" ] && [ ! $NODE_V_MINOR = "v0.12" ]; then
|
||||
if [ ! $NODE_V_MINOR = "v0.10" ] && [ ! $NODE_V_MINOR = "v0.11" ] && [ ! $NODE_V_MINOR = "v0.12" ] && [ ! $NODE_V_MAIN = "v4" ] && [ ! $NODE_V_MAIN = "v5" ]; then
|
||||
if [ ! $IOJS_VERSION ]; then
|
||||
echo "You're running a wrong version of node, or io.js is not installed. You're using $NODE_VERSION, we need v0.10.x, v0.11.x or v0.12.x" >&2
|
||||
echo "You're running a wrong version of node, or io.js is not installed. You're using $NODE_VERSION, we need node v0.10.x or higher" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
|
|
@ -6,11 +6,6 @@ cd /D "%~dp0\.."
|
|||
:: Is node installed?
|
||||
cmd /C node -e "" || ( echo "Please install node.js ( http://nodejs.org )" && exit /B 1 )
|
||||
|
||||
echo _
|
||||
echo Checking node version...
|
||||
set check_version="if(['10','11','12'].indexOf(process.version.split('.')[1]) === -1 && process.version.split('.')[0] !== '1') { console.log('You are running a wrong version of Node. Etherpad requires v0.10+'); process.exit(1) }"
|
||||
cmd /C node -e %check_version% || exit /B 1
|
||||
|
||||
echo _
|
||||
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
|
||||
|
|
|
@ -79,6 +79,9 @@ async.series([
|
|||
newPad.pool.numToAttrib = oldPad.pool.numToAttrib;
|
||||
for(var curRevNum = 0; curRevNum <= newRevHead; curRevNum++) {
|
||||
db.db.get("pad:" + padId + ":revs:" + curRevNum, function(err, rev) {
|
||||
if (rev.meta) {
|
||||
throw "The specified revision number could not be found.";
|
||||
}
|
||||
var newRevNum = ++newPad.head;
|
||||
var newRevId = "pad:" + newPad.id + ":revs:" + newRevNum;
|
||||
db.db.set(newRevId, rev);
|
||||
|
|
|
@ -339,3 +339,14 @@ Things in context:
|
|||
|
||||
This hook is provided to allow author highlight style to be modified.
|
||||
Registered hooks should return 1 if the plugin handles highlighting. If no plugin returns 1, the core will use the default background-based highlighting.
|
||||
|
||||
## aceSelectionChanged
|
||||
Called from: src/static/js/ace2_inner.js
|
||||
|
||||
Things in context:
|
||||
|
||||
1. rep - information about where the user's cursor is
|
||||
2. documentAttributeManager - information about attributes in the document
|
||||
|
||||
This hook allows a plugin to react to a cursor or selection change,
|
||||
perhaps to update a UI element based on the style at the cursor location.
|
||||
|
|
|
@ -81,7 +81,7 @@ Available blocks in `pad.html` are:
|
|||
* `modals` - Contains all connectivity messages
|
||||
* `embedPopup` - the embed dropdown
|
||||
* `scripts` - Add your script tags here, if you really have to (consider use client-side hooks instead)
|
||||
|
||||
|
||||
`timeslider.html` blocks:
|
||||
|
||||
* `timesliderStyles`
|
||||
|
@ -90,9 +90,9 @@ Available blocks in `pad.html` are:
|
|||
* `timesliderTop`
|
||||
* `timesliderEditbarRight`
|
||||
* `modals`
|
||||
|
||||
|
||||
`index.html` blocks:
|
||||
|
||||
|
||||
* `indexWrapper` - contains the form for creating new pads
|
||||
|
||||
## padInitToolbar
|
||||
|
@ -334,7 +334,7 @@ exports.aceAttribClasses = function(hook_name, attr, cb){
|
|||
```
|
||||
|
||||
## exportFileName
|
||||
Called from src/node/handler/ExportHandler.js
|
||||
Called from src/node/handler/ExportHandler.js
|
||||
|
||||
Things in context:
|
||||
|
||||
|
@ -357,7 +357,7 @@ Things in context:
|
|||
|
||||
1. Pad object
|
||||
|
||||
This hook will allow a plug-in developer to include more properties and attributes to support during HTML Export. An Array should be returned.
|
||||
This hook will allow a plug-in developer to include more properties and attributes to support during HTML Export. If tags are stored as `['color', 'red']` on the attribute pool, use `exportHtmlAdditionalTagsWithData` instead. An Array should be returned.
|
||||
|
||||
Example:
|
||||
```
|
||||
|
@ -368,6 +368,24 @@ exports.exportHtmlAdditionalTags = function(hook, pad, cb){
|
|||
};
|
||||
```
|
||||
|
||||
## exportHtmlAdditionalTagsWithData
|
||||
Called from src/node/utils/ExportHtml.js
|
||||
|
||||
Things in context:
|
||||
|
||||
1. Pad object
|
||||
|
||||
Identical to `exportHtmlAdditionalTags`, but for tags that are stored with an specific value (not simply `true`) on the attribute pool. For example `['color', 'red']`, instead of `['bold', true]`. This hook will allow a plug-in developer to include more properties and attributes to support during HTML Export. An Array of arrays should be returned. The exported HTML will contain tags like `<span data-color="red">` for the content where attributes are `['color', 'red']`.
|
||||
|
||||
Example:
|
||||
```
|
||||
// Add the props to be supported in export
|
||||
exports.exportHtmlAdditionalTagsWithData = function(hook, pad, cb){
|
||||
var padId = pad.id;
|
||||
cb([["color", "red"], ["color", "blue"]]);
|
||||
};
|
||||
```
|
||||
|
||||
## userLeave
|
||||
Called from src/node/handler/PadMessageHandler.js
|
||||
|
||||
|
@ -384,3 +402,20 @@ exports.userLeave = function(hook, session, callback) {
|
|||
console.log('%s left pad %s', session.author, session.padId);
|
||||
};
|
||||
```
|
||||
|
||||
### clientReady
|
||||
Called from src/node/handler/PadMessageHandler.js
|
||||
|
||||
This in context:
|
||||
|
||||
1. message
|
||||
|
||||
This hook gets called when handling a CLIENT_READY which is the first message from the client to the server.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
exports.clientReady = function(hook, message) {
|
||||
console.log('Client has entered the pad' + message.padId);
|
||||
};
|
||||
```
|
||||
|
|
|
@ -86,10 +86,14 @@
|
|||
may cause problems during deployment. Set to 0 to disable caching */
|
||||
"maxAge" : 21600, // 60 * 60 * 6 = 6 hours
|
||||
|
||||
/* This is the path to the Abiword executable. Setting it to null, disables abiword.
|
||||
/* This is the absolute path to the Abiword executable. Setting it to null, disables abiword.
|
||||
Abiword is needed to advanced import/export features of pads*/
|
||||
"abiword" : null,
|
||||
|
||||
/* This is the absolute path to the soffice executable. Setting it to null, disables LibreOffice exporting.
|
||||
LibreOffice can be used in lieu of Abiword to export pads */
|
||||
"soffice" : null,
|
||||
|
||||
/* This is the path to the Tidy executable. Setting it to null, disables Tidy.
|
||||
Tidy is used to improve the quality of exported pads*/
|
||||
"tidyHtml" : null,
|
||||
|
@ -131,6 +135,11 @@
|
|||
// Allow Load Testing tools to hit the Etherpad Instance. Warning this will disable security on the instance.
|
||||
"loadTest": false,
|
||||
|
||||
// Disable indentation on new line when previous line ends with some special chars (':', '[', '(', '{')
|
||||
/*
|
||||
"indentationOnNewLine": false,
|
||||
*/
|
||||
|
||||
/* The toolbar buttons configuration.
|
||||
"toolbar": {
|
||||
"left": [
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
"Alami",
|
||||
"Meno25",
|
||||
"Test Create account",
|
||||
"محمد أحمد عبد الفتاح"
|
||||
"محمد أحمد عبد الفتاح",
|
||||
"Haytham morsy"
|
||||
]
|
||||
},
|
||||
"index.newPad": "باد جديد",
|
||||
|
@ -97,6 +98,9 @@
|
|||
"timeslider.exportCurrent": "تصدير النسخة الحالية ك:",
|
||||
"timeslider.version": "إصدار {{version}}",
|
||||
"timeslider.saved": "محفوظ {{month}} {{day}}, {{year}}",
|
||||
"timeslider.playPause": "تشغيل / إيقاف مؤقت محتويات الباد",
|
||||
"timeslider.backRevision": "عد إلى مراجعة في هذه الباد",
|
||||
"timeslider.forwardRevision": "انطلق إلى مراجعة في هذه الباد",
|
||||
"timeslider.dateformat": "{{day}}/{{month}}/{{year}} {{hours}}:{{minutes}}:{{seconds}}",
|
||||
"timeslider.month.january": "يناير",
|
||||
"timeslider.month.february": "فبراير",
|
||||
|
|
|
@ -132,7 +132,7 @@
|
|||
"pad.impexp.confirmimport": "Al importar un archivo se borrará el contenido actual del pad. ¿Estás seguro de que quieres continuar?",
|
||||
"pad.impexp.convertFailed": "No pudimos importar este archivo. Inténtalo con un formato diferente o copia y pega manualmente.",
|
||||
"pad.impexp.padHasData": "No hemos podido importar este archivo porque este pad ya ha tenido cambios. Importa a un nuevo pad.",
|
||||
"pad.impexp.uploadFailed": "El envío falló. Intentalo de nuevo.",
|
||||
"pad.impexp.uploadFailed": "El envío falló. Inténtalo de nuevo.",
|
||||
"pad.impexp.importfailed": "Fallo al importar",
|
||||
"pad.impexp.copypaste": "Intenta copiar y pegar",
|
||||
"pad.impexp.exportdisabled": "La exportación al formato {{type}} está desactivada. Contacta a tu administrador de sistemas."
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
"@metadata": {
|
||||
"authors": [
|
||||
"Denö",
|
||||
"Mashoi7"
|
||||
"Mashoi7",
|
||||
"Ilja.mos"
|
||||
]
|
||||
},
|
||||
"pad.toolbar.underline.title": "Alleviivua (Ctrl+U)",
|
||||
|
@ -36,7 +37,7 @@
|
|||
"timeslider.month.january": "pakkaskuudu",
|
||||
"timeslider.month.february": "tuhukuudu",
|
||||
"timeslider.month.march": "kevätkuudu",
|
||||
"timeslider.month.april": "kevätkuudu",
|
||||
"timeslider.month.april": "sulakuudu",
|
||||
"timeslider.month.may": "oraskuudu",
|
||||
"timeslider.month.june": "kezäkuudu",
|
||||
"timeslider.month.july": "heinykuudu",
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
"@metadata": {
|
||||
"authors": [
|
||||
"Aalam",
|
||||
"Babanwalia"
|
||||
"Babanwalia",
|
||||
"ਪ੍ਰਚਾਰਕ"
|
||||
]
|
||||
},
|
||||
"index.newPad": "ਨਵਾਂ ਪੈਡ",
|
||||
|
@ -10,29 +11,31 @@
|
|||
"pad.toolbar.bold.title": "ਗੂੜ੍ਹਾ (Ctrl-B)",
|
||||
"pad.toolbar.italic.title": "ਤਿਰਛਾ (Ctrl-I)",
|
||||
"pad.toolbar.underline.title": "ਹੇਠਾਂ-ਰੇਖਾ (Ctrl-U)",
|
||||
"pad.toolbar.strikethrough.title": "ਵਿੰਨ੍ਹੋ ਵਿਨੋ",
|
||||
"pad.toolbar.ol.title": "ਲੜੀਵਾਰ ਲਿਸਟ",
|
||||
"pad.toolbar.strikethrough.title": "ਵਿੰਨ੍ਹੋ (Ctrl+5)",
|
||||
"pad.toolbar.ol.title": "ਲੜੀਵਾਰ ਸੂਚੀ",
|
||||
"pad.toolbar.ul.title": "ਬਿਨ-ਲੜੀਬੱਧ ਸੂਚੀ",
|
||||
"pad.toolbar.indent.title": "ਹਾਸ਼ੀਏ ਤੋਂ ਪਰ੍ਹੇ (ਟੈਬ)",
|
||||
"pad.toolbar.unindent.title": "ਹਾਸ਼ੀਏ ਵੱਲ (ਸ਼ਿਫ਼ਟ+ਟੈਬ)",
|
||||
"pad.toolbar.undo.title": "ਵਾਪਸ (Ctrl-Z)",
|
||||
"pad.toolbar.redo.title": "ਪਰਤਾਓ (Ctrl-Y)",
|
||||
"pad.toolbar.clearAuthorship.title": "ਪਰਮਾਣਕਿਤਾ ਰੰਗ ਸਾਫ਼ ਕਰੋ",
|
||||
"pad.toolbar.clearAuthorship.title": "ਪਰਮਾਣਕਿਤਾ ਰੰਗ ਸਾਫ਼ ਕਰੋ (Ctrl+Shift+C)",
|
||||
"pad.toolbar.import_export.title": "ਵੱਖ-ਵੱਖ ਫਾਇਲ ਫਾਰਮੈਟ ਤੋਂ/ਵਿੱਚ ਇੰਪੋਰਟ/ਐਕਸਪੋਰਟ ਕਰੋ",
|
||||
"pad.toolbar.timeslider.title": "ਸਮਾਂ-ਲਕੀਰ",
|
||||
"pad.toolbar.savedRevision.title": "ਰੀਵਿਜ਼ਨ ਸੰਭਾਲੋ",
|
||||
"pad.toolbar.settings.title": "ਸੈਟਿੰਗ",
|
||||
"pad.toolbar.embed.title": "ਇਹ ਪੈਡ ਸਾਂਝਾ ਤੇ ਇੰਬੈੱਡ ਕਰੋ",
|
||||
"pad.toolbar.showusers.title": "ਇਹ ਪੈਡ ਉੱਤੇ ਯੂਜ਼ਰ ਵੇਖਾਓ",
|
||||
"pad.colorpicker.save": "ਸਾਂਭੋ",
|
||||
"pad.colorpicker.save": "ਸੰਭਾਲੋ",
|
||||
"pad.colorpicker.cancel": "ਰੱਦ ਕਰੋ",
|
||||
"pad.loading": "…ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ",
|
||||
"pad.noCookie": "ਕੂਕੀਜ਼ ਨਹੀਂ ਲੱਭੀਅਾਂ। ਕਿਰਪਾ ਕਰਕੇ ਬ੍ਰਾੳੂਜ਼ਰ ਵਿੱਚ ਕੂਕੀਜ਼ ਲਾਗੂ ਕਰੋ।",
|
||||
"pad.passwordRequired": "ਇਹ ਪੈਡ ਦੀ ਵਰਤੋਂ ਕਰਨ ਲਈ ਤੁਹਾਨੂੰ ਪਾਸਵਰਡ ਚਾਹੀਦਾ ਹੈ",
|
||||
"pad.permissionDenied": "ਇਹ ਪੈਡ ਵਰਤਨ ਲਈ ਤੁਹਾਨੂੰ ਅਧਿਕਾਰ ਨਹੀਂ ਹਨ",
|
||||
"pad.wrongPassword": "ਤੁਹਾਡਾ ਪਾਸਵਰਡ ਗਲਤੀ ਸੀ",
|
||||
"pad.settings.padSettings": "ਪੈਡ ਸੈਟਿੰਗ",
|
||||
"pad.settings.myView": "ਮੇਰੀ ਝਲਕ",
|
||||
"pad.settings.stickychat": "ਹਮੇਸ਼ਾ ਸਕਰੀਨ ਉੱਤੇ ਗੱਲ ਕਰੋ",
|
||||
"pad.settings.chatandusers": "ਗੱਲ-ਬਾਤ ਅਤੇ ਵਰਤੋਂਕਾਰ ਦਿਖਾਵੋ",
|
||||
"pad.settings.colorcheck": "ਲੇਖਕੀ ਰੰਗ",
|
||||
"pad.settings.linenocheck": "ਲਾਈਨ ਨੰਬਰ",
|
||||
"pad.settings.rtlcheck": "ਸਮੱਗਰੀ ਸੱਜੇ ਤੋਂ ਖੱਬੇ ਪੜ੍ਹਨੀ ਹੈ?",
|
||||
|
@ -45,10 +48,11 @@
|
|||
"pad.importExport.import": "ਕੋਈ ਵੀ ਟੈਕਸਟ ਫਾਇਲ ਜਾਂ ਦਸਤਾਵੇਜ਼ ਅੱਪਲੋਡ ਕਰੋ",
|
||||
"pad.importExport.importSuccessful": "ਸਫ਼ਲ!",
|
||||
"pad.importExport.export": "ਮੌਜੂਦਾ ਪੈਡ ਨੂੰ ਐਕਸਪੋਰਟ ਕਰੋ:",
|
||||
"pad.importExport.exportetherpad": "ੲੈਥਰਪੈਡ",
|
||||
"pad.importExport.exporthtml": "HTML",
|
||||
"pad.importExport.exportplain": "ਸਧਾਰਨ ਟੈਕਸਟ",
|
||||
"pad.importExport.exportword": "ਮਾਈਕਰੋਸਾਫਟ ਵਰਡ",
|
||||
"pad.importExport.exportpdf": "ਪੀਡੀਐਫ",
|
||||
"pad.importExport.exportpdf": "PDF",
|
||||
"pad.importExport.exportopen": "ODF (ਓਪਨ ਡੌਕੂਮੈਂਟ ਫਾਰਮੈਟ)",
|
||||
"pad.importExport.abiword.innerHTML": "ਤੁਸੀਂ ਸਿਰਫ਼ ਸਾਦੀਆਂ ਲਿਖਤੀ ਜਾਂ ਐੱਚ.ਟੀ.ਐੱਮ.ਐੱਲ. ਰੂਪ-ਰੇਖਾਵਾਂ ਤੋਂ ਦਰਾਮਦ ਕਰ ਸਕਦੇ ਹੋ। ਹੋਰ ਉੱਨਤ ਦਰਾਮਦੀ ਗੁਣਾਂ ਵਾਸਤੇ ਮਿਹਰਬਾਨੀ ਕਰਕੇ <a href=\"https://github.com/ether/etherpad-lite/wiki/How-to-enable-importing-and-exporting-different-file-formats-in-Ubuntu-or-OpenSuse-or-SLES-with-AbiWord\">ਐਬੀਵਰਡ ਥਾਪੋ</a>।",
|
||||
"pad.modals.connected": "ਕੁਨੈਕਟ ਹੈ।",
|
||||
|
@ -88,8 +92,11 @@
|
|||
"timeslider.toolbar.authorsList": "ਕੋਈ ਲੇਖਕ ਨਹੀਂ",
|
||||
"timeslider.toolbar.exportlink.title": "ਐਕਸਪੋਰਟ",
|
||||
"timeslider.exportCurrent": "ਮੌਜੂਦਾ ਵਰਜਨ ਇੰਝ ਐਕਸਪੋਰਟ ਕਰੋ:",
|
||||
"timeslider.version": "ਵਰਜਨ {{version}}",
|
||||
"timeslider.version": "ਵਰਜ਼ਨ {{version}}",
|
||||
"timeslider.saved": "{{day}} {{month}} {{year}} ਨੂੰ ਸੰਭਾਲਿਆ",
|
||||
"timeslider.playPause": "ਪੈਡ ਸਮੱਗਰੀ ਚਲਾਓ / ਵਿਰਾਮ ਕਰੋ",
|
||||
"timeslider.backRevision": "ਇਸ ਪੈਡ ਵਿੱਚ ਪਿਛਲੇ ਰੀਵਿਜ਼ਨ ਤੇ ਜਾਓ",
|
||||
"timeslider.forwardRevision": "ਇਸ ਪੈਡ ਵਿੱਚ ਅਗਲੇ ਰੀਵਿਜ਼ਨ ਤੇ ਜਾਓ",
|
||||
"timeslider.dateformat": "{{day}}/{{month}}/{{year}} {{hours}}:{{minutes}}:{{seconds}}",
|
||||
"timeslider.month.january": "ਜਨਵਰੀ",
|
||||
"timeslider.month.february": "ਫ਼ਰਵਰੀ",
|
||||
|
@ -118,5 +125,5 @@
|
|||
"pad.impexp.uploadFailed": "ਅੱਪਲੋਡ ਲਈ ਫੇਲ੍ਹ ਹੈ, ਫੇਰ ਕੋਸ਼ਿਸ਼ ਕਰੋ ਜੀ।",
|
||||
"pad.impexp.importfailed": "ਇੰਪੋਰਟ ਫੇਲ੍ਹ ਹੈ",
|
||||
"pad.impexp.copypaste": "ਕਾਪੀ ਕਰੋ ਚੇਪੋ ਜੀ",
|
||||
"pad.impexp.exportdisabled": "{{type}} ਰੂਪ-ਰੇਖਾ ਵਜੋਂ ਬਰਾਮਦ ਕਰਨਾ ਬੰਦ ਹੈ। ਵੇਰਵੇ ਵਾਸਤੇ ਮਿਹਰਬਾਨੀ ਕਰਕੇ ਆਪਣੇ ਸਿਸਟਮ ਦੇ ਪ੍ਰਬੰਧਕ ਨਾਲ਼ ਰਾਬਤਾ ਬਣਾਉ।"
|
||||
"pad.impexp.exportdisabled": "{{type}} ਫਾਰਮੈਟ ਵਜੋਂ ਬਰਾਮਦ ਕਰਨਾ ਬੰਦ ਹੈ। ਵੇਰਵੇ ਵਾਸਤੇ ਆਪਣੇ ਸਿਸਟਮ ਦੇ ਪਰਬੰਧਕ ਨਾਲ ਸੰਪਰਕ ਕਰੋ।"
|
||||
}
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
"Hedwig",
|
||||
"ImGelu",
|
||||
"Minisarm",
|
||||
"Strainu"
|
||||
"Strainu",
|
||||
"Wintereu"
|
||||
]
|
||||
},
|
||||
"index.newPad": "Pad nou",
|
||||
|
@ -21,6 +22,7 @@
|
|||
"pad.toolbar.import_export.title": "Importă/Exportă din/în diferite formate",
|
||||
"pad.toolbar.savedRevision.title": "Salvează revizia",
|
||||
"pad.toolbar.settings.title": "Setări",
|
||||
"pad.toolbar.showusers.title": "Arată utilizatorii de pe acest pad",
|
||||
"pad.colorpicker.save": "Salvează",
|
||||
"pad.colorpicker.cancel": "Anulează",
|
||||
"pad.loading": "Se încarcă...",
|
||||
|
@ -46,10 +48,10 @@
|
|||
"pad.importExport.exportpdf": "PDF",
|
||||
"pad.importExport.exportopen": "ODF (Open Document Format)",
|
||||
"pad.modals.connected": "Conectat.",
|
||||
"pad.modals.reconnecting": "Se reconectează la pad-ul tău..",
|
||||
"pad.modals.reconnecting": "Se reconectează la pad-ul dumneavoastră..",
|
||||
"pad.modals.forcereconnect": "Forțează reconectarea",
|
||||
"pad.modals.userdup": "Deschis în altă fereastră",
|
||||
"pad.modals.userdup.advice": "Reconectează pentru a folosi această fereastră în schimb",
|
||||
"pad.modals.userdup.advice": "Reconectați-vă dacă doriți să utilizați această fereastră.",
|
||||
"pad.modals.unauth": "Nu ești autorizat",
|
||||
"pad.modals.initsocketfail": "Serverul nu este disponibil.",
|
||||
"pad.modals.initsocketfail.explanation": "Nu s-a putut conecta la serverul de sincronizare.",
|
||||
|
@ -61,12 +63,12 @@
|
|||
"pad.share": "Distribuie acest pad",
|
||||
"pad.share.readonly": "Doar în citire",
|
||||
"pad.share.link": "Legătură",
|
||||
"pad.share.emebdcode": "Încorporează URL-ul",
|
||||
"pad.share.emebdcode": "Adresa URL încorporată",
|
||||
"pad.chat": "Chat",
|
||||
"pad.chat.title": "Deschide chat-ul pentru acest pad.",
|
||||
"pad.chat.loadmessages": "Încarcă mai multe mesaje",
|
||||
"timeslider.toolbar.returnbutton": "Înapoi la pad",
|
||||
"timeslider.toolbar.authors": "Aurori:",
|
||||
"timeslider.toolbar.authors": "Autori:",
|
||||
"timeslider.toolbar.authorsList": "Niciun autor",
|
||||
"timeslider.toolbar.exportlink.title": "Exportă",
|
||||
"timeslider.exportCurrent": "Exportă versiunea curentă ca:",
|
||||
|
@ -85,7 +87,7 @@
|
|||
"timeslider.month.october": "octombrie",
|
||||
"timeslider.month.november": "noiembrie",
|
||||
"timeslider.month.december": "decembrie",
|
||||
"pad.userlist.entername": "Introdu numele tău",
|
||||
"pad.userlist.entername": "Introduceți numele dumneavoastră",
|
||||
"pad.userlist.unnamed": "fără nume",
|
||||
"pad.userlist.guest": "Oaspete",
|
||||
"pad.userlist.deny": "Respinge",
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
"Kosovastar"
|
||||
]
|
||||
},
|
||||
"index.newPad": "Bllok i Ri",
|
||||
"index.newPad": "Bllok i ri",
|
||||
"index.createOpenPad": "ose krijoni/hapni një Bllok me emrin:",
|
||||
"pad.toolbar.bold.title": "Të trasha (Ctrl-B)",
|
||||
"pad.toolbar.italic.title": "Të pjerrëta (Ctrl-I)",
|
||||
|
@ -13,7 +13,7 @@
|
|||
"pad.toolbar.strikethrough.title": "Hequrvije (Ctrl+5)",
|
||||
"pad.toolbar.ol.title": "Listë e renditur (Ctrl+Shift+N)",
|
||||
"pad.toolbar.ul.title": "Listë e parenditur (Ctrl+Shift+L)",
|
||||
"pad.toolbar.indent.title": "Brendazi (TAB)",
|
||||
"pad.toolbar.indent.title": "E dhëmbëzuar (TAB)",
|
||||
"pad.toolbar.unindent.title": "Jashtazi (Shift+TAB)",
|
||||
"pad.toolbar.undo.title": "Zhbëje (Ctrl-Z)",
|
||||
"pad.toolbar.redo.title": "Ribëje (Ctrl-Y)",
|
||||
|
@ -21,7 +21,7 @@
|
|||
"pad.toolbar.import_export.title": "Importoni/Eksportoni nga/në formate të tjera kartelash",
|
||||
"pad.toolbar.timeslider.title": "Rrjedha kohore",
|
||||
"pad.toolbar.savedRevision.title": "Ruaje Rishikimin",
|
||||
"pad.toolbar.settings.title": "Rregullime",
|
||||
"pad.toolbar.settings.title": "Parametrat",
|
||||
"pad.toolbar.embed.title": "Ndajeni me të tjerët dhe Trupëzojeni këtë bllok",
|
||||
"pad.toolbar.showusers.title": "Shfaq përdoruesit në këtë bllok",
|
||||
"pad.colorpicker.save": "Ruaje",
|
||||
|
@ -45,6 +45,7 @@
|
|||
"pad.importExport.import": "Ngarkoni cilëndo kartelë teksti ose dokument",
|
||||
"pad.importExport.importSuccessful": "Me sukses!",
|
||||
"pad.importExport.export": "Eksportojeni bllokun e tanishëm si:",
|
||||
"pad.importExport.exportetherpad": "Etherpad",
|
||||
"pad.importExport.exporthtml": "HTML",
|
||||
"pad.importExport.exportplain": "Tekst të thjeshtë",
|
||||
"pad.importExport.exportword": "Microsoft Word",
|
||||
|
@ -58,7 +59,7 @@
|
|||
"pad.modals.userdup.explanation": "Ky bllok duket se gjendet i hapur në më shumë se një dritare shfletuesi në këtë kompjuter.",
|
||||
"pad.modals.userdup.advice": "Rilidhuni që të përdoret kjo dritare.",
|
||||
"pad.modals.unauth": "I paautorizuar",
|
||||
"pad.modals.unauth.explanation": "Ndërkohë që shihnit këtë dritare, lejet tuaja kanë ndryshuar. Provoni të rilidheni.",
|
||||
"pad.modals.unauth.explanation": "Ndërkohë që sheh këtë dritare, lejet e tua kanë ndryshuar. Provo të rilidhesh.",
|
||||
"pad.modals.looping.explanation": "Ka probleme komunikimi me shërbyesin e njëkohësimit.",
|
||||
"pad.modals.looping.cause": "Ndoshta jeni lidhur përmes një firewall-i ose ndërmjetësi të papërputhshëm.",
|
||||
"pad.modals.initsocketfail": "Nuk kapet dot shërbyesi.",
|
||||
|
@ -90,6 +91,7 @@
|
|||
"timeslider.exportCurrent": "Eksportojeni versionin e tanishëm si:",
|
||||
"timeslider.version": "Versioni {{version}}",
|
||||
"timeslider.saved": "Ruajtur më {{month}} {{day}}, {{year}}",
|
||||
"timeslider.playPause": "Luaj përmbajtjet e Pad / Pauzo",
|
||||
"timeslider.dateformat": "{{month}}/{{day}}/{{year}} {{hours}}:{{minutes}}:{{seconds}}",
|
||||
"timeslider.month.january": "Janar",
|
||||
"timeslider.month.february": "Shkurt",
|
||||
|
|
|
@ -82,7 +82,7 @@
|
|||
"pad.modals.badChangeset.cause": "这可能是因为服务器配置的错误或者其他未预料到的行为。如果您认为这是错误,请联系服务管理员。要继续编辑,请尝试重新连接。",
|
||||
"pad.modals.corruptPad.explanation": "您试图连接的记事本已损坏。",
|
||||
"pad.modals.corruptPad.cause": "这可能是因为服务器配置的错误或者其他未预料到的行为。请联系服务管理员。",
|
||||
"pad.modals.deleted": "已刪除。",
|
||||
"pad.modals.deleted": "已删除。",
|
||||
"pad.modals.deleted.explanation": "此记事本已被移除。",
|
||||
"pad.modals.disconnected": "您已断开连接。",
|
||||
"pad.modals.disconnected.explanation": "到服务器的连接已丢失",
|
||||
|
|
|
@ -307,6 +307,38 @@ exports.setText = function(padID, text, callback)
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
appendText(padID, text) appends text to a pad
|
||||
|
||||
Example returns:
|
||||
|
||||
{code: 0, message:"ok", data: null}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
{code: 1, message:"text too long", data: null}
|
||||
*/
|
||||
exports.appendText = function(padID, text, callback)
|
||||
{
|
||||
//text is required
|
||||
if(typeof text != "string")
|
||||
{
|
||||
callback(new customError("text is no string","apierror"));
|
||||
return;
|
||||
}
|
||||
|
||||
//get the pad
|
||||
getPadSafe(padID, true, function(err, pad)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
|
||||
pad.appendText(text);
|
||||
|
||||
//update the clients on the pad
|
||||
padMessageHandler.updatePadClients(pad, callback);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
getHTML(padID, [rev]) returns the html of a pad
|
||||
|
||||
|
|
|
@ -127,7 +127,7 @@ exports.createAuthor = function(name, callback)
|
|||
var author = "a." + randomString(16);
|
||||
|
||||
//create the globalAuthors db entry
|
||||
var authorObj = {"colorId" : Math.floor(Math.random()*32), "name": name, "timestamp": new Date().getTime()};
|
||||
var authorObj = {"colorId" : Math.floor(Math.random()*(exports.getColorPalette().length)), "name": name, "timestamp": new Date().getTime()};
|
||||
|
||||
//set the global author db entry
|
||||
db.set("globalAuthor:" + author, authorObj);
|
||||
|
|
|
@ -303,6 +303,19 @@ Pad.prototype.setText = function setText(newText) {
|
|||
this.appendRevision(changeset);
|
||||
};
|
||||
|
||||
Pad.prototype.appendText = function appendText(newText) {
|
||||
//clean the new text
|
||||
newText = exports.cleanText(newText);
|
||||
|
||||
var oldText = this.text();
|
||||
|
||||
//create the changeset
|
||||
var changeset = Changeset.makeSplice(oldText, oldText.length, 0, newText);
|
||||
|
||||
//append the changeset
|
||||
this.appendRevision(changeset);
|
||||
};
|
||||
|
||||
Pad.prototype.appendChatMessage = function appendChatMessage(text, userId, time) {
|
||||
this.chatHead++;
|
||||
//save the chat entry in the database
|
||||
|
|
|
@ -444,10 +444,61 @@ var version =
|
|||
, "getChatHead" : ["padID"]
|
||||
, "restoreRevision" : ["padID", "rev"]
|
||||
}
|
||||
, "1.2.13":
|
||||
{ "createGroup" : []
|
||||
, "createGroupIfNotExistsFor" : ["groupMapper"]
|
||||
, "deleteGroup" : ["groupID"]
|
||||
, "listPads" : ["groupID"]
|
||||
, "listAllPads" : []
|
||||
, "createDiffHTML" : ["padID", "startRev", "endRev"]
|
||||
, "createPad" : ["padID", "text"]
|
||||
, "createGroupPad" : ["groupID", "padName", "text"]
|
||||
, "createAuthor" : ["name"]
|
||||
, "createAuthorIfNotExistsFor": ["authorMapper" , "name"]
|
||||
, "listPadsOfAuthor" : ["authorID"]
|
||||
, "createSession" : ["groupID", "authorID", "validUntil"]
|
||||
, "deleteSession" : ["sessionID"]
|
||||
, "getSessionInfo" : ["sessionID"]
|
||||
, "listSessionsOfGroup" : ["groupID"]
|
||||
, "listSessionsOfAuthor" : ["authorID"]
|
||||
, "getText" : ["padID", "rev"]
|
||||
, "setText" : ["padID", "text"]
|
||||
, "getHTML" : ["padID", "rev"]
|
||||
, "setHTML" : ["padID", "html"]
|
||||
, "getAttributePool" : ["padID"]
|
||||
, "getRevisionsCount" : ["padID"]
|
||||
, "getSavedRevisionsCount" : ["padID"]
|
||||
, "listSavedRevisions" : ["padID"]
|
||||
, "saveRevision" : ["padID", "rev"]
|
||||
, "getRevisionChangeset" : ["padID", "rev"]
|
||||
, "getLastEdited" : ["padID"]
|
||||
, "deletePad" : ["padID"]
|
||||
, "copyPad" : ["sourceID", "destinationID", "force"]
|
||||
, "movePad" : ["sourceID", "destinationID", "force"]
|
||||
, "getReadOnlyID" : ["padID"]
|
||||
, "getPadID" : ["roID"]
|
||||
, "setPublicStatus" : ["padID", "publicStatus"]
|
||||
, "getPublicStatus" : ["padID"]
|
||||
, "setPassword" : ["padID", "password"]
|
||||
, "isPasswordProtected" : ["padID"]
|
||||
, "listAuthorsOfPad" : ["padID"]
|
||||
, "padUsersCount" : ["padID"]
|
||||
, "getAuthorName" : ["authorID"]
|
||||
, "padUsers" : ["padID"]
|
||||
, "sendClientsMessage" : ["padID", "msg"]
|
||||
, "listAllGroups" : []
|
||||
, "checkToken" : []
|
||||
, "appendChatMessage" : ["padID", "text", "authorID", "time"]
|
||||
, "getChatHistory" : ["padID"]
|
||||
, "getChatHistory" : ["padID", "start", "end"]
|
||||
, "getChatHead" : ["padID"]
|
||||
, "restoreRevision" : ["padID", "rev"]
|
||||
, "appendText" : ["padID", "text"]
|
||||
}
|
||||
};
|
||||
|
||||
// set the latest available API version here
|
||||
exports.latestApiVersion = '1.2.12';
|
||||
exports.latestApiVersion = '1.2.13';
|
||||
|
||||
// exports the versions so it can be used by the new Swagger endpoint
|
||||
exports.version = version;
|
||||
|
|
|
@ -30,9 +30,15 @@ var os = require('os');
|
|||
var hooks = require("ep_etherpad-lite/static/js/pluginfw/hooks");
|
||||
var TidyHtml = require('../utils/TidyHtml');
|
||||
|
||||
var convertor = null;
|
||||
|
||||
//load abiword only if its enabled
|
||||
if(settings.abiword != null)
|
||||
var abiword = require("../utils/Abiword");
|
||||
convertor = require("../utils/Abiword");
|
||||
|
||||
// Use LibreOffice if an executable has been defined in the settings
|
||||
if(settings.soffice != null)
|
||||
convertor = require("../utils/LibreOffice");
|
||||
|
||||
var tempDirectory = "/tmp";
|
||||
|
||||
|
@ -70,71 +76,11 @@ exports.doExport = function(req, res, padId, type)
|
|||
}
|
||||
else if(type == "txt")
|
||||
{
|
||||
var txt;
|
||||
var randNum;
|
||||
var srcFile, destFile;
|
||||
|
||||
async.series([
|
||||
//render the txt document
|
||||
function(callback)
|
||||
{
|
||||
exporttxt.getPadTXTDocument(padId, req.params.rev, false, function(err, _txt)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
txt = _txt;
|
||||
callback();
|
||||
});
|
||||
},
|
||||
//decide what to do with the txt export
|
||||
function(callback)
|
||||
{
|
||||
//if this is a txt export, we can send this from here directly
|
||||
res.send(txt);
|
||||
callback("stop");
|
||||
},
|
||||
//send the convert job to abiword
|
||||
function(callback)
|
||||
{
|
||||
//ensure html can be collected by the garbage collector
|
||||
txt = null;
|
||||
|
||||
destFile = tempDirectory + "/etherpad_export_" + randNum + "." + type;
|
||||
abiword.convertFile(srcFile, destFile, type, callback);
|
||||
},
|
||||
//send the file
|
||||
function(callback)
|
||||
{
|
||||
res.sendFile(destFile, null, callback);
|
||||
},
|
||||
//clean up temporary files
|
||||
function(callback)
|
||||
{
|
||||
async.parallel([
|
||||
function(callback)
|
||||
{
|
||||
fs.unlink(srcFile, callback);
|
||||
},
|
||||
function(callback)
|
||||
{
|
||||
//100ms delay to accomidate for slow windows fs
|
||||
if(os.type().indexOf("Windows") > -1)
|
||||
{
|
||||
setTimeout(function()
|
||||
{
|
||||
fs.unlink(destFile, callback);
|
||||
}, 100);
|
||||
}
|
||||
else
|
||||
{
|
||||
fs.unlink(destFile, callback);
|
||||
}
|
||||
}
|
||||
], callback);
|
||||
}
|
||||
], function(err)
|
||||
exporttxt.getPadTXTDocument(padId, req.params.rev, false, function(err, txt)
|
||||
{
|
||||
if(err && err != "stop") ERR(err);
|
||||
})
|
||||
if(ERR(err)) return;
|
||||
res.send(txt);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -183,11 +129,11 @@ exports.doExport = function(req, res, padId, type)
|
|||
TidyHtml.tidy(srcFile, callback);
|
||||
},
|
||||
|
||||
//send the convert job to abiword
|
||||
//send the convert job to the convertor (abiword, libreoffice, ..)
|
||||
function(callback)
|
||||
{
|
||||
destFile = tempDirectory + "/etherpad_export_" + randNum + "." + type;
|
||||
abiword.convertFile(srcFile, destFile, type, callback);
|
||||
convertor.convertFile(srcFile, destFile, type, callback);
|
||||
},
|
||||
//send the file
|
||||
function(callback)
|
||||
|
|
|
@ -630,8 +630,8 @@ function handleUserChanges(data, cb)
|
|||
messageLogger.warn("Dropped message, USER_CHANGES Message has no changeset!");
|
||||
return cb();
|
||||
}
|
||||
//TODO: this might happen with other messages too => find one place to copy the session
|
||||
//and always use the copy. atm a message will be ignored if the session is gone even
|
||||
//TODO: this might happen with other messages too => find one place to copy the session
|
||||
//and always use the copy. atm a message will be ignored if the session is gone even
|
||||
//if the session was valid when the message arrived in the first place
|
||||
if(!sessioninfos[client.id])
|
||||
{
|
||||
|
@ -960,7 +960,7 @@ function handleSwitchToPad(client, message)
|
|||
roomClients[i].leave(padId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// start up the new pad
|
||||
createSessionInfo(client, message);
|
||||
handleClientReady(client, message);
|
||||
|
@ -1020,6 +1020,8 @@ function handleClientReady(client, message)
|
|||
var currentTime;
|
||||
var padIds;
|
||||
|
||||
hooks.callAll("clientReady", message);
|
||||
|
||||
async.series([
|
||||
//Get ro/rw id:s
|
||||
function (callback)
|
||||
|
@ -1229,6 +1231,7 @@ function handleClientReady(client, message)
|
|||
"plugins": plugins.plugins,
|
||||
"parts": plugins.parts,
|
||||
},
|
||||
"indentationOnNewLine": settings.indentationOnNewLine,
|
||||
"initialChangesets": [] // FIXME: REMOVE THIS SHIT
|
||||
}
|
||||
|
||||
|
@ -1365,6 +1368,12 @@ function handleChangesetRequest(client, message)
|
|||
messageLogger.warn("Dropped message, changeset request has no granularity!");
|
||||
return;
|
||||
}
|
||||
//https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger#Polyfill
|
||||
if(Math.floor(message.data.granularity) !== message.data.granularity)
|
||||
{
|
||||
messageLogger.warn("Dropped message, changeset request granularity is not an integer!");
|
||||
return;
|
||||
}
|
||||
if(message.data.start == null)
|
||||
{
|
||||
messageLogger.warn("Dropped message, changeset request has no start!");
|
||||
|
|
|
@ -16,6 +16,7 @@ exports.expressCreateServer = function (hook_name, args, cb) {
|
|||
if(sanitizedPadId != padId)
|
||||
{
|
||||
var real_url = sanitizedPadId;
|
||||
real_url = encodeURIComponent(real_url);
|
||||
var query = url.parse(req.url).query;
|
||||
if ( query ) real_url += '?' + query;
|
||||
res.header('Location', real_url);
|
||||
|
|
|
@ -19,6 +19,7 @@ var async = require("async");
|
|||
var Changeset = require("ep_etherpad-lite/static/js/Changeset");
|
||||
var padManager = require("../db/PadManager");
|
||||
var ERR = require("async-stacktrace");
|
||||
var _ = require('underscore');
|
||||
var Security = require('ep_etherpad-lite/static/js/security');
|
||||
var hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks');
|
||||
var _analyzeLine = require('./ExportHelper')._analyzeLine;
|
||||
|
@ -77,12 +78,21 @@ function getHTMLFromAtext(pad, atext, authorColors)
|
|||
var tags = ['h1', 'h2', 'strong', 'em', 'u', 's'];
|
||||
var props = ['heading1', 'heading2', 'bold', 'italic', 'underline', 'strikethrough'];
|
||||
|
||||
// prepare tags stored as ['tag', true] to be exported
|
||||
hooks.aCallAll("exportHtmlAdditionalTags", pad, function(err, newProps){
|
||||
newProps.forEach(function (propName, i){
|
||||
tags.push(propName);
|
||||
props.push(propName);
|
||||
});
|
||||
});
|
||||
// prepare tags stored as ['tag', 'value'] to be exported. This will generate HTML
|
||||
// with tags like <span data-tag="value">
|
||||
hooks.aCallAll("exportHtmlAdditionalTagsWithData", pad, function(err, newProps){
|
||||
newProps.forEach(function (propName, i){
|
||||
tags.push('span data-' + propName[0] + '="' + propName[1] + '"');
|
||||
props.push(propName);
|
||||
});
|
||||
});
|
||||
|
||||
// holds a map of used styling attributes (*1, *2, etc) in the apool
|
||||
// and maps them to an index in props
|
||||
|
@ -115,8 +125,8 @@ function getHTMLFromAtext(pad, atext, authorColors)
|
|||
var newLength = props.push(propName);
|
||||
anumMap[a] = newLength -1;
|
||||
|
||||
css+=".removed {text-decoration: line-through; " +
|
||||
"-ms-filter:'progid:DXImageTransform.Microsoft.Alpha(Opacity=80)'; "+
|
||||
css+=".removed {text-decoration: line-through; " +
|
||||
"-ms-filter:'progid:DXImageTransform.Microsoft.Alpha(Opacity=80)'; "+
|
||||
"filter: alpha(opacity=80); "+
|
||||
"opacity: 0.8; "+
|
||||
"}\n";
|
||||
|
@ -130,7 +140,13 @@ function getHTMLFromAtext(pad, atext, authorColors)
|
|||
// this pad, and if yes puts its attrib id->props value into anumMap
|
||||
props.forEach(function (propName, i)
|
||||
{
|
||||
var propTrueNum = apool.putAttrib([propName, true], true);
|
||||
var attrib = [propName, true];
|
||||
if (_.isArray(propName)) {
|
||||
// propName can be in the form of ['color', 'red'],
|
||||
// see hook exportHtmlAdditionalTagsWithData
|
||||
attrib = propName;
|
||||
}
|
||||
var propTrueNum = apool.putAttrib(attrib, true);
|
||||
if (propTrueNum >= 0)
|
||||
{
|
||||
anumMap[propTrueNum] = i;
|
||||
|
@ -154,6 +170,12 @@ function getHTMLFromAtext(pad, atext, authorColors)
|
|||
|
||||
var property = props[i];
|
||||
|
||||
// we are not insterested on properties in the form of ['color', 'red'],
|
||||
// see hook exportHtmlAdditionalTagsWithData
|
||||
if (_.isArray(property)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(property.substr(0,6) === "author"){
|
||||
return stripDotFromAuthorID(property);
|
||||
}
|
||||
|
@ -165,6 +187,13 @@ function getHTMLFromAtext(pad, atext, authorColors)
|
|||
return false;
|
||||
}
|
||||
|
||||
// tags added by exportHtmlAdditionalTagsWithData will be exported as <span> with
|
||||
// data attributes
|
||||
function isSpanWithData(i){
|
||||
var property = props[i];
|
||||
return _.isArray(property);
|
||||
}
|
||||
|
||||
function emitOpenTag(i)
|
||||
{
|
||||
openTags.unshift(i);
|
||||
|
@ -186,8 +215,9 @@ function getHTMLFromAtext(pad, atext, authorColors)
|
|||
{
|
||||
openTags.shift();
|
||||
var spanClass = getSpanClassFor(i);
|
||||
var spanWithData = isSpanWithData(i);
|
||||
|
||||
if(spanClass){
|
||||
if(spanClass || spanWithData){
|
||||
assem.append('</span>');
|
||||
} else {
|
||||
assem.append('</');
|
||||
|
@ -263,7 +293,7 @@ function getHTMLFromAtext(pad, atext, authorColors)
|
|||
|
||||
var s = taker.take(chars);
|
||||
|
||||
//removes the characters with the code 12. Don't know where they come
|
||||
//removes the characters with the code 12. Don't know where they come
|
||||
//from but they break the abiword parser and are completly useless
|
||||
s = s.replace(String.fromCharCode(12), "");
|
||||
|
||||
|
@ -377,7 +407,7 @@ function getHTMLFromAtext(pad, atext, authorColors)
|
|||
pieces.push('<br><br>');
|
||||
}
|
||||
}*/
|
||||
else//means we are getting closer to the lowest level of indentation or are at the same level
|
||||
else//means we are getting closer to the lowest level of indentation or are at the same level
|
||||
{
|
||||
var toClose = lists.length > 0 ? listLevels[listLevels.length - 2] - line.listLevel : 0
|
||||
if( toClose > 0){
|
||||
|
@ -431,7 +461,7 @@ function getHTMLFromAtext(pad, atext, authorColors)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (var k = lists.length - 1; k >= 0; k--)
|
||||
{
|
||||
if(lists[k][1] == "number")
|
||||
|
@ -460,14 +490,17 @@ exports.getPadHTMLDocument = function (padId, revNum, noDocType, callback)
|
|||
stylesForExportCSS += css;
|
||||
});
|
||||
// Core inclusion of head etc.
|
||||
var head =
|
||||
(noDocType ? '' : '<!doctype html>\n') +
|
||||
'<html lang="en">\n' + (noDocType ? '' : '<head>\n' +
|
||||
var head =
|
||||
(noDocType ? '' : '<!doctype html>\n') +
|
||||
'<html lang="en">\n' + (noDocType ? '' : '<head>\n' +
|
||||
'<title>' + Security.escapeHTML(padId) + '</title>\n' +
|
||||
'<meta charset="utf-8">\n' +
|
||||
'<style> * { font-family: arial, sans-serif;\n' +
|
||||
'font-size: 13px;\n' +
|
||||
'line-height: 17px; }' +
|
||||
'<meta name="generator" content="Etherpad">\n' +
|
||||
'<meta name="author" content="Etherpad">\n' +
|
||||
'<meta name="changedby" content="Etherpad">\n' +
|
||||
'<meta charset="utf-8">\n' +
|
||||
'<style> * { font-family: arial, sans-serif;\n' +
|
||||
'font-size: 13px;\n' +
|
||||
'line-height: 17px; }' +
|
||||
'ul.indent { list-style-type: none; }' +
|
||||
|
||||
'ol { list-style-type: none; padding-left:0;}' +
|
||||
|
@ -553,8 +586,8 @@ exports.getPadHTMLDocument = function (padId, revNum, noDocType, callback)
|
|||
'ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol{ text-indent: 140px; }' +
|
||||
'ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol > ol{ text-indent: 150px; }' +
|
||||
|
||||
stylesForExportCSS +
|
||||
'</style>\n' + '</head>\n') +
|
||||
stylesForExportCSS +
|
||||
'</style>\n' + '</head>\n') +
|
||||
'<body>';
|
||||
var foot = '</body>\n</html>\n';
|
||||
|
||||
|
|
93
src/node/utils/LibreOffice.js
Normal file
93
src/node/utils/LibreOffice.js
Normal file
|
@ -0,0 +1,93 @@
|
|||
/**
|
||||
* Controls the communication with LibreOffice
|
||||
*/
|
||||
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
var async = require("async");
|
||||
var fs = require("fs");
|
||||
var os = require("os");
|
||||
var path = require("path");
|
||||
var settings = require("./Settings");
|
||||
var spawn = require("child_process").spawn;
|
||||
|
||||
// Conversion tasks will be queued up, so we don't overload the system
|
||||
var queue = async.queue(doConvertTask, 1);
|
||||
|
||||
/**
|
||||
* Convert a file from one type to another
|
||||
*
|
||||
* @param {String} srcFile The path on disk to convert
|
||||
* @param {String} destFile The path on disk where the converted file should be stored
|
||||
* @param {String} type The type to convert into
|
||||
* @param {Function} callback Standard callback function
|
||||
*/
|
||||
exports.convertFile = function(srcFile, destFile, type, callback) {
|
||||
queue.push({"srcFile": srcFile, "destFile": destFile, "type": type, "callback": callback});
|
||||
};
|
||||
|
||||
function doConvertTask(task, callback) {
|
||||
var tmpDir = os.tmpdir();
|
||||
|
||||
async.series([
|
||||
// Generate a PDF file with LibreOffice
|
||||
function(callback) {
|
||||
var soffice = spawn(settings.soffice, [
|
||||
'--headless',
|
||||
'--invisible',
|
||||
'--nologo',
|
||||
'--nolockcheck',
|
||||
'--convert-to', task.type,
|
||||
task.srcFile,
|
||||
'--outdir', tmpDir
|
||||
]);
|
||||
|
||||
var stdoutBuffer = '';
|
||||
|
||||
// Delegate the processing of stdout to another function
|
||||
soffice.stdout.on('data', function(data) {
|
||||
stdoutBuffer += data.toString();
|
||||
});
|
||||
|
||||
// Append error messages to the buffer
|
||||
soffice.stderr.on('data', function(data) {
|
||||
stdoutBuffer += data.toString();
|
||||
});
|
||||
|
||||
// Throw an exception if libreoffice failed
|
||||
soffice.on('exit', function(code) {
|
||||
if (code != 0) {
|
||||
return callback("LibreOffice died with exit code " + code + " and message: " + stdoutBuffer);
|
||||
}
|
||||
|
||||
callback();
|
||||
})
|
||||
},
|
||||
|
||||
// Move the PDF file to the correct place
|
||||
function(callback) {
|
||||
var filename = path.basename(task.srcFile);
|
||||
var pdfFilename = filename.substr(0, filename.lastIndexOf('.')) + '.' + task.type;
|
||||
var pdfPath = path.join(tmpDir, pdfFilename);
|
||||
fs.rename(pdfPath, task.destFile, callback);
|
||||
}
|
||||
], function(err) {
|
||||
// Invoke the callback for the local queue
|
||||
callback();
|
||||
|
||||
// Invoke the callback for the task
|
||||
task.callback(err);
|
||||
});
|
||||
}
|
|
@ -152,6 +152,11 @@ exports.minify = true;
|
|||
*/
|
||||
exports.abiword = null;
|
||||
|
||||
/**
|
||||
* The path of the libreoffice executable
|
||||
*/
|
||||
exports.soffice = null;
|
||||
|
||||
/**
|
||||
* The path of the tidy executable
|
||||
*/
|
||||
|
@ -177,6 +182,11 @@ exports.disableIPlogging = false;
|
|||
*/
|
||||
exports.loadTest = false;
|
||||
|
||||
/**
|
||||
* Enable indentation on new lines
|
||||
*/
|
||||
exports.indentationOnNewLine = true;
|
||||
|
||||
/*
|
||||
* log4js appender configuration
|
||||
*/
|
||||
|
@ -218,8 +228,14 @@ exports.getGitCommit = function() {
|
|||
try
|
||||
{
|
||||
var rootPath = path.resolve(npm.dir, '..');
|
||||
var ref = fs.readFileSync(rootPath + "/.git/HEAD", "utf-8");
|
||||
var refPath = rootPath + "/.git/" + ref.substring(5, ref.indexOf("\n"));
|
||||
if (fs.lstatSync(rootPath + '/.git').isFile()) {
|
||||
rootPath = fs.readFileSync(rootPath + '/.git', "utf8");
|
||||
rootPath = rootPath.split(' ').pop().trim();
|
||||
} else {
|
||||
rootPath += '/.git';
|
||||
}
|
||||
var ref = fs.readFileSync(rootPath + "/HEAD", "utf-8");
|
||||
var refPath = rootPath + "/" + ref.substring(5, ref.indexOf("\n"));
|
||||
version = fs.readFileSync(refPath, "utf-8");
|
||||
version = version.substring(0, 7);
|
||||
}
|
||||
|
|
|
@ -265,7 +265,7 @@ plugins.ensure(function () {\n\
|
|||
iframeHTML: iframeHTML
|
||||
});
|
||||
|
||||
iframeHTML.push('</head><body id="innerdocbody" role="application" class="syntax" spellcheck="false"> </body></html>');
|
||||
iframeHTML.push('</head><body id="innerdocbody" class="innerdocbody" role="application" class="syntax" spellcheck="false"> </body></html>');
|
||||
|
||||
// Expose myself to global for my child frame.
|
||||
var thisFunctionsName = "ChildAccessibleAce2Editor";
|
||||
|
@ -315,7 +315,7 @@ window.onload = function () {\n\
|
|||
|
||||
// bizarrely, in FF2, a file with no "external" dependencies won't finish loading properly
|
||||
// (throbs busy while typing)
|
||||
outerHTML.push('<style type="text/css" title="dynamicsyntax"></style>', '<link rel="stylesheet" type="text/css" href="data:text/css,"/>', scriptTag(outerScript), '</head><body id="outerdocbody"><div id="sidediv"><!-- --></div><div id="linemetricsdiv">x</div></body></html>');
|
||||
outerHTML.push('<style type="text/css" title="dynamicsyntax"></style>', '<link rel="stylesheet" type="text/css" href="data:text/css,"/>', scriptTag(outerScript), '</head><body id="outerdocbody" class="outerdocbody"><div id="sidediv" class="sidediv"><!-- --></div><div id="linemetricsdiv">x</div></body></html>');
|
||||
|
||||
var outerFrame = document.createElement("IFRAME");
|
||||
outerFrame.name = "ace_outer";
|
||||
|
|
|
@ -1894,7 +1894,11 @@ function Ace2Inner(){
|
|||
var prevLine = rep.lines.prev(thisLine);
|
||||
var prevLineText = prevLine.text;
|
||||
var theIndent = /^ *(?:)/.exec(prevLineText)[0];
|
||||
if (/[\[\(\:\{]\s*$/.exec(prevLineText)) theIndent += THE_TAB;
|
||||
var shouldIndent = parent.parent.clientVars.indentationOnNewLine;
|
||||
if (shouldIndent && /[\[\(\:\{]\s*$/.exec(prevLineText))
|
||||
{
|
||||
theIndent += THE_TAB;
|
||||
}
|
||||
var cs = Changeset.builder(rep.lines.totalWidth()).keep(
|
||||
rep.lines.offsetOfIndex(lineNum), lineNum).insert(
|
||||
theIndent, [
|
||||
|
@ -2336,7 +2340,7 @@ function Ace2Inner(){
|
|||
|
||||
function getAttributeOnSelection(attributeName){
|
||||
if (!(rep.selStart && rep.selEnd)) return
|
||||
|
||||
|
||||
var withIt = Changeset.makeAttribsString('+', [
|
||||
[attributeName, 'true']
|
||||
], rep.apool);
|
||||
|
@ -2347,14 +2351,14 @@ function Ace2Inner(){
|
|||
}
|
||||
|
||||
return rangeHasAttrib(rep.selStart, rep.selEnd)
|
||||
|
||||
|
||||
function rangeHasAttrib(selStart, selEnd) {
|
||||
// if range is collapsed -> no attribs in range
|
||||
if(selStart[1] == selEnd[1] && selStart[0] == selEnd[0]) return false
|
||||
|
||||
|
||||
if(selStart[0] != selEnd[0]) { // -> More than one line selected
|
||||
var hasAttrib = true
|
||||
|
||||
|
||||
// from selStart to the end of the first line
|
||||
hasAttrib = hasAttrib && rangeHasAttrib(selStart, [selStart[0], rep.lines.atIndex(selStart[0]).text.length])
|
||||
|
||||
|
@ -2365,22 +2369,22 @@ function Ace2Inner(){
|
|||
|
||||
// for the last, potentially partial, line
|
||||
hasAttrib = hasAttrib && rangeHasAttrib([selEnd[0], 0], [selEnd[0], selEnd[1]])
|
||||
|
||||
|
||||
return hasAttrib
|
||||
}
|
||||
|
||||
|
||||
// Logic tells us we now have a range on a single line
|
||||
|
||||
|
||||
var lineNum = selStart[0]
|
||||
, start = selStart[1]
|
||||
, end = selEnd[1]
|
||||
, hasAttrib = true
|
||||
|
||||
|
||||
// Iterate over attribs on this line
|
||||
|
||||
|
||||
var opIter = Changeset.opIterator(rep.alines[lineNum])
|
||||
, indexIntoLine = 0
|
||||
|
||||
|
||||
while (opIter.hasNext()) {
|
||||
var op = opIter.next();
|
||||
var opStartInLine = indexIntoLine;
|
||||
|
@ -2394,11 +2398,11 @@ function Ace2Inner(){
|
|||
}
|
||||
indexIntoLine = opEndInLine;
|
||||
}
|
||||
|
||||
|
||||
return hasAttrib
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
editorInfo.ace_getAttributeOnSelection = getAttributeOnSelection;
|
||||
|
||||
function toggleAttributeOnSelection(attributeName)
|
||||
|
@ -2897,6 +2901,12 @@ function Ace2Inner(){
|
|||
rep.selFocusAtStart = newSelFocusAtStart;
|
||||
currentCallStack.repChanged = true;
|
||||
|
||||
hooks.callAll('aceSelectionChanged', {
|
||||
rep: rep,
|
||||
callstack: currentCallStack,
|
||||
documentAttributeManager: documentAttributeManager,
|
||||
});
|
||||
|
||||
return true;
|
||||
//console.log("selStart: %o, selEnd: %o, focusAtStart: %s", rep.selStart, rep.selEnd,
|
||||
//String(!!rep.selFocusAtStart));
|
||||
|
@ -3632,16 +3642,10 @@ function Ace2Inner(){
|
|||
var altKey = evt.altKey;
|
||||
var shiftKey = evt.shiftKey;
|
||||
|
||||
// prevent ESC key
|
||||
if (keyCode == 27)
|
||||
{
|
||||
evt.preventDefault();
|
||||
return;
|
||||
}
|
||||
// Is caret potentially hidden by the chat button?
|
||||
var myselection = document.getSelection(); // get the current caret selection
|
||||
var caretOffsetTop = myselection.focusNode.parentNode.offsetTop | myselection.focusNode.offsetTop; // get the carets selection offset in px IE 214
|
||||
|
||||
|
||||
if(myselection.focusNode.wholeText){ // Is there any content? If not lineHeight will report wrong..
|
||||
var lineHeight = myselection.focusNode.parentNode.offsetHeight; // line height of populated links
|
||||
}else{
|
||||
|
@ -3706,20 +3710,25 @@ function Ace2Inner(){
|
|||
documentAttributeManager: documentAttributeManager,
|
||||
evt:evt
|
||||
});
|
||||
specialHandled = (specialHandledInHook&&specialHandledInHook.length>0)?specialHandledInHook[0]:specialHandled;
|
||||
|
||||
// if any hook returned true, set specialHandled with true
|
||||
if (specialHandledInHook) {
|
||||
specialHandled = _.contains(specialHandledInHook, true);
|
||||
}
|
||||
|
||||
if ((!specialHandled) && altKey && isTypeForSpecialKey && keyCode == 120){
|
||||
// Alt F9 focuses on the File Menu and/or editbar.
|
||||
// Note that while most editors use Alt F10 this is not desirable
|
||||
// As ubuntu cannot use Alt F10....
|
||||
// Focus on the editbar. -- TODO: Move Focus back to previous state (we know it so we can use it)
|
||||
var firstEditbarElement = parent.parent.$('#editbar').children("ul").first().children().first().children().first().children().first();
|
||||
$(this).blur();
|
||||
$(this).blur();
|
||||
firstEditbarElement.focus();
|
||||
evt.preventDefault();
|
||||
}
|
||||
if ((!specialHandled) && altKey && keyCode == 67 && type === "keydown"){
|
||||
// Alt c focuses on the Chat window
|
||||
$(this).blur();
|
||||
$(this).blur();
|
||||
parent.parent.chat.show();
|
||||
parent.parent.$("#chatinput").focus();
|
||||
evt.preventDefault();
|
||||
|
@ -3830,6 +3839,15 @@ function Ace2Inner(){
|
|||
}, 0);
|
||||
specialHandled = true;
|
||||
}
|
||||
if ((!specialHandled) && isTypeForSpecialKey && keyCode == 27)
|
||||
{
|
||||
// prevent esc key;
|
||||
// in mozilla versions 14-19 avoid reconnecting pad.
|
||||
|
||||
fastIncorp(4);
|
||||
evt.preventDefault();
|
||||
specialHandled = true;
|
||||
}
|
||||
if ((!specialHandled) && isTypeForCmdKey && String.fromCharCode(which).toLowerCase() == "s" && (evt.metaKey || evt.ctrlKey) && !evt.altKey) /* Do a saved revision on ctrl S */
|
||||
{
|
||||
evt.preventDefault();
|
||||
|
@ -4961,7 +4979,7 @@ function Ace2Inner(){
|
|||
|
||||
// Disabled: https://github.com/ether/etherpad-lite/issues/2546
|
||||
// Will break OL re-numbering: https://github.com/ether/etherpad-lite/pull/2533
|
||||
// $(document).on("cut", handleCut);
|
||||
// $(document).on("cut", handleCut);
|
||||
|
||||
$(root).on("blur", handleBlur);
|
||||
if (browser.msie)
|
||||
|
@ -4972,12 +4990,19 @@ function Ace2Inner(){
|
|||
|
||||
// Don't paste on middle click of links
|
||||
$(root).on("paste", function(e){
|
||||
// TODO: this breaks pasting strings into URLS when using
|
||||
// TODO: this breaks pasting strings into URLS when using
|
||||
// Control C and Control V -- the Event is never available
|
||||
// here.. :(
|
||||
if(e.target.a || e.target.localName === "a"){
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
// Call paste hook
|
||||
hooks.callAll('acePaste', {
|
||||
editorInfo: editorInfo,
|
||||
rep: rep,
|
||||
documentAttributeManager: documentAttributeManager
|
||||
});
|
||||
})
|
||||
|
||||
// CompositionEvent is not implemented below IE version 8
|
||||
|
@ -5347,8 +5372,9 @@ function Ace2Inner(){
|
|||
function initLineNumbers()
|
||||
{
|
||||
lineNumbersShown = 1;
|
||||
sideDiv.innerHTML = '<table border="0" cellpadding="0" cellspacing="0" align="right"><tr><td id="sidedivinner"><div>1</div></td></tr></table>';
|
||||
sideDiv.innerHTML = '<table border="0" cellpadding="0" cellspacing="0" align="right"><tr><td id="sidedivinner" class="sidedivinner"><div>1</div></td></tr></table>';
|
||||
sideDivInner = outerWin.document.getElementById("sidedivinner");
|
||||
$(sideDiv).addClass("sidediv");
|
||||
}
|
||||
|
||||
function updateLineNumbers()
|
||||
|
|
|
@ -11,7 +11,7 @@ $(document).ready(function () {
|
|||
|
||||
//connect
|
||||
var room = url + "pluginfw/installer";
|
||||
socket = io.connect(room, {resource : resource});
|
||||
socket = io.connect(room, {path: baseURL + "socket.io", resource : resource});
|
||||
|
||||
function search(searchTerm, limit) {
|
||||
if(search.searchTerm != searchTerm) {
|
||||
|
|
|
@ -10,7 +10,7 @@ $(document).ready(function () {
|
|||
|
||||
//connect
|
||||
var room = url + "settings";
|
||||
socket = io.connect(room, {resource : resource});
|
||||
socket = io.connect(room, {path: baseURL + "socket.io", resource : resource});
|
||||
|
||||
socket.on('settings', function (settings) {
|
||||
|
||||
|
|
|
@ -100,7 +100,7 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas
|
|||
function textify(str)
|
||||
{
|
||||
return sanitizeUnicode(
|
||||
str.replace(/\n/g, '').replace(/[\n\r ]/g, ' ').replace(/\xa0/g, ' ').replace(/\t/g, ' '));
|
||||
str.replace(/(\n | \n)/g, ' ').replace(/[\n\r ]/g, ' ').replace(/\xa0/g, ' ').replace(/\t/g, ' '));
|
||||
}
|
||||
|
||||
function getAssoc(node, name)
|
||||
|
|
|
@ -100,7 +100,7 @@
|
|||
</div>
|
||||
|
||||
<div id="editorcontainerbox">
|
||||
<div id="editorcontainer"></div>
|
||||
<div id="editorcontainer" class="editorcontainer"></div>
|
||||
<div id="editorloadingbox">
|
||||
<div id="passwordRequired">
|
||||
<p data-l10n-id="pad.passwordRequired">You need a password to access this pad</p>
|
||||
|
|
|
@ -79,6 +79,8 @@ describe('Permission', function(){
|
|||
-> movePad(newPadID, originalPadId) -- Should provide consistant pad data
|
||||
-> getText(originalPadId) -- Should be "hello world"
|
||||
-> getLastEdited(padID) -- Should not be 0
|
||||
-> appendText(padID, "hello")
|
||||
-> getText(padID) -- Should be "hello worldhello"
|
||||
-> setHTML(padID) -- Should fail on invalid HTML
|
||||
-> setHTML(padID) *3 -- Should fail on invalid HTML
|
||||
-> getHTML(padID) -- Should return HTML close to posted HTML
|
||||
|
@ -483,6 +485,30 @@ describe('getLastEdited', function(){
|
|||
});
|
||||
})
|
||||
|
||||
describe('appendText', function(){
|
||||
it('Append text to a pad Id', function(done) {
|
||||
api.get(endPoint('appendText', '1.2.13')+"&padID="+testPadId+"&text=hello")
|
||||
.expect(function(res){
|
||||
if(res.body.code !== 0) throw new Error("Pad Append Text failed");
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200, done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getText', function(){
|
||||
it('Gets text on a pad Id', function(done) {
|
||||
api.get(endPoint('getText')+"&padID="+testPadId)
|
||||
.expect(function(res){
|
||||
if(res.body.code !== 0) throw new Error("Pad Get Text failed");
|
||||
if(res.body.data.text !== text+"\nhello") throw new Error("Pad Text not set properly");
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200, done);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('setHTML', function(){
|
||||
it('Sets the HTML of a Pad attempting to pass ugly HTML', function(done) {
|
||||
var html = "<div><b>Hello HTML</title></head></div>";
|
||||
|
@ -542,8 +568,9 @@ describe('createPad', function(){
|
|||
|
||||
*/
|
||||
|
||||
var endPoint = function(point){
|
||||
return '/api/'+apiVersion+'/'+point+'?apikey='+apiKey;
|
||||
var endPoint = function(point, version){
|
||||
version = version || apiVersion;
|
||||
return '/api/'+version+'/'+point+'?apikey='+apiKey;
|
||||
}
|
||||
|
||||
function makeid()
|
||||
|
|
|
@ -15,7 +15,7 @@ describe("indentation button", function(){
|
|||
//select this text element
|
||||
$firstTextElement.sendkeys('{selectall}');
|
||||
|
||||
if(inner$(window)[0].bowser.firefox || inner$(window)[0].bowser.modernIE){ // if it's a mozilla or IE
|
||||
if(inner$(window)[0].bowser.firefox || inner$(window)[0].bowser.modernIE){ // if it's a mozilla or IE
|
||||
var evtType = "keypress";
|
||||
}else{
|
||||
var evtType = "keydown";
|
||||
|
@ -31,7 +31,7 @@ describe("indentation button", function(){
|
|||
});
|
||||
|
||||
it("indent text with button", function(done){
|
||||
var inner$ = helper.padInner$;
|
||||
var inner$ = helper.padInner$;
|
||||
var chrome$ = helper.padChrome$;
|
||||
|
||||
var $indentButton = chrome$(".buttonicon-indent");
|
||||
|
@ -43,7 +43,7 @@ describe("indentation button", function(){
|
|||
});
|
||||
|
||||
it("keeps the indent on enter for the new line", function(done){
|
||||
var inner$ = helper.padInner$;
|
||||
var inner$ = helper.padInner$;
|
||||
var chrome$ = helper.padChrome$;
|
||||
|
||||
var $indentButton = chrome$(".buttonicon-indent");
|
||||
|
@ -51,9 +51,9 @@ describe("indentation button", function(){
|
|||
|
||||
//type a bit, make a line break and type again
|
||||
var $firstTextElement = inner$("div span").first();
|
||||
$firstTextElement.sendkeys('line 1');
|
||||
$firstTextElement.sendkeys('{enter}');
|
||||
$firstTextElement.sendkeys('line 2');
|
||||
$firstTextElement.sendkeys('line 1');
|
||||
$firstTextElement.sendkeys('{enter}');
|
||||
$firstTextElement.sendkeys('line 2');
|
||||
$firstTextElement.sendkeys('{enter}');
|
||||
|
||||
helper.waitFor(function(){
|
||||
|
@ -68,13 +68,87 @@ describe("indentation button", function(){
|
|||
});
|
||||
});
|
||||
|
||||
it("indents text with spaces on enter if previous line ends with ':', '[', '(', or '{'", function(done){
|
||||
var inner$ = helper.padInner$;
|
||||
var chrome$ = helper.padChrome$;
|
||||
|
||||
//type a bit, make a line break and type again
|
||||
var $firstTextElement = inner$("div").first();
|
||||
$firstTextElement.sendkeys("line with ':'{enter}");
|
||||
$firstTextElement.sendkeys("line with '['{enter}");
|
||||
$firstTextElement.sendkeys("line with '('{enter}");
|
||||
$firstTextElement.sendkeys("line with '{{}'{enter}");
|
||||
|
||||
helper.waitFor(function(){
|
||||
// wait for Etherpad to split four lines into separated divs
|
||||
var $fourthLine = inner$("div").first().next().next().next();
|
||||
return $fourthLine.text().indexOf("line with '{'") === 0;
|
||||
}).done(function(){
|
||||
// we validate bottom to top for easier implementation
|
||||
|
||||
// curly braces
|
||||
var $lineWithCurlyBraces = inner$("div").first().next().next().next();
|
||||
$lineWithCurlyBraces.sendkeys('{{}');
|
||||
pressEnter(); // cannot use sendkeys('{enter}') here, browser does not read the command properly
|
||||
var $lineAfterCurlyBraces = inner$("div").first().next().next().next().next();
|
||||
expect($lineAfterCurlyBraces.text()).to.match(/\s{4}/); // tab === 4 spaces
|
||||
|
||||
// parenthesis
|
||||
var $lineWithParenthesis = inner$("div").first().next().next();
|
||||
$lineWithParenthesis.sendkeys('(');
|
||||
pressEnter();
|
||||
var $lineAfterParenthesis = inner$("div").first().next().next().next();
|
||||
expect($lineAfterParenthesis.text()).to.match(/\s{4}/);
|
||||
|
||||
// bracket
|
||||
var $lineWithBracket = inner$("div").first().next();
|
||||
$lineWithBracket.sendkeys('[');
|
||||
pressEnter();
|
||||
var $lineAfterBracket = inner$("div").first().next().next();
|
||||
expect($lineAfterBracket.text()).to.match(/\s{4}/);
|
||||
|
||||
// colon
|
||||
var $lineWithColon = inner$("div").first();
|
||||
$lineWithColon.sendkeys(':');
|
||||
pressEnter();
|
||||
var $lineAfterColon = inner$("div").first().next();
|
||||
expect($lineAfterColon.text()).to.match(/\s{4}/);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("appends indentation to the indent of previous line if previous line ends with ':', '[', '(', or '{'", function(done){
|
||||
var inner$ = helper.padInner$;
|
||||
var chrome$ = helper.padChrome$;
|
||||
|
||||
//type a bit, make a line break and type again
|
||||
var $firstTextElement = inner$("div").first();
|
||||
$firstTextElement.sendkeys(" line with some indentation and ':'{enter}");
|
||||
$firstTextElement.sendkeys("line 2{enter}");
|
||||
|
||||
helper.waitFor(function(){
|
||||
// wait for Etherpad to split two lines into separated divs
|
||||
var $secondLine = inner$("div").first().next();
|
||||
return $secondLine.text().indexOf("line 2") === 0;
|
||||
}).done(function(){
|
||||
var $lineWithColon = inner$("div").first();
|
||||
$lineWithColon.sendkeys(':');
|
||||
pressEnter();
|
||||
var $lineAfterColon = inner$("div").first().next();
|
||||
expect($lineAfterColon.text()).to.match(/\s{6}/); // previous line indentation + regular tab (4 spaces)
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
|
||||
it("makes text indented and outdented", function() {
|
||||
|
||||
//get the inner iframe
|
||||
var $inner = testHelper.$getPadInner();
|
||||
|
||||
|
||||
//get the first text element out of the inner iframe
|
||||
var firstTextElement = $inner.find("div").first();
|
||||
|
||||
|
@ -87,7 +161,7 @@ describe("indentation button", function(){
|
|||
|
||||
//ace creates a new dom element when you press a button, so just get the first text element again
|
||||
var newFirstTextElement = $inner.find("div").first();
|
||||
|
||||
|
||||
// is there a list-indent class element now?
|
||||
var firstChild = newFirstTextElement.children(":first");
|
||||
var isUL = firstChild.is('ul');
|
||||
|
@ -160,12 +234,12 @@ describe("indentation button", function(){
|
|||
/* this test creates the below content, both should have double indentation
|
||||
line1
|
||||
line2
|
||||
|
||||
|
||||
|
||||
firstTextElement.sendkeys('{rightarrow}'); // simulate a keypress of enter
|
||||
firstTextElement.sendkeys('{enter}'); // simulate a keypress of enter
|
||||
firstTextElement.sendkeys('line 1'); // simulate writing the first line
|
||||
firstTextElement.sendkeys('{enter}'); // simulate a keypress of enter
|
||||
firstTextElement.sendkeys('{enter}'); // simulate a keypress of enter
|
||||
firstTextElement.sendkeys('line 2'); // simulate writing the second line
|
||||
|
||||
//get the second text element out of the inner iframe
|
||||
|
@ -203,3 +277,15 @@ describe("indentation button", function(){
|
|||
});*/
|
||||
|
||||
});
|
||||
|
||||
function pressEnter(){
|
||||
var inner$ = helper.padInner$;
|
||||
if(inner$(window)[0].bowser.firefox || inner$(window)[0].bowser.modernIE){ // if it's a mozilla or IE
|
||||
var evtType = "keypress";
|
||||
}else{
|
||||
var evtType = "keydown";
|
||||
}
|
||||
var e = inner$.Event(evtType);
|
||||
e.keyCode = 13; // enter :|
|
||||
inner$("#innerdocbody").trigger(e);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue