Merge pull request #1 from ether/develop

Update to most recent etherpad-lite version
This commit is contained in:
fcassin 2015-11-10 15:50:40 +01:00
commit f2836125dc
32 changed files with 574 additions and 171 deletions

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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.

View file

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

View file

@ -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": [

View file

@ -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": "فبراير",

View file

@ -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."

View file

@ -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",

View file

@ -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}} ਫਾਰਮੈਟ ਵਜੋਂ ਬਰਾਮਦ ਕਰਨਾ ਬੰਦ ਹੈ। ਵੇਰਵੇ ਵਾਸਤੇ ਆਪਣੇ ਸਿਸਟਮ ਦੇ ਪਰਬੰਧਕ ਨਾਲ ਸੰਪਰਕ ਕਰੋ।"
}

View file

@ -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",

View file

@ -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",

View file

@ -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": "到服务器的连接已丢失",

View file

@ -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

View file

@ -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);

View file

@ -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

View file

@ -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;

View file

@ -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)
{
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
if(ERR(err)) return;
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)
{
if(err && err != "stop") ERR(err);
})
});
}
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)

View file

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

View file

@ -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);

View file

@ -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
@ -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('</');
@ -464,6 +494,9 @@ exports.getPadHTMLDocument = function (padId, revNum, noDocType, callback)
(noDocType ? '' : '<!doctype html>\n') +
'<html lang="en">\n' + (noDocType ? '' : '<head>\n' +
'<title>' + Security.escapeHTML(padId) + '</title>\n' +
'<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' +

View 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);
});
}

View file

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

View file

@ -265,7 +265,7 @@ plugins.ensure(function () {\n\
iframeHTML: iframeHTML
});
iframeHTML.push('</head><body id="innerdocbody" role="application" class="syntax" spellcheck="false">&nbsp;</body></html>');
iframeHTML.push('</head><body id="innerdocbody" class="innerdocbody" role="application" class="syntax" spellcheck="false">&nbsp;</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";

View file

@ -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, [
@ -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,12 +3642,6 @@ 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
@ -3706,7 +3710,12 @@ 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
@ -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();
@ -4978,6 +4996,13 @@ function Ace2Inner(){
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()

View file

@ -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) {

View file

@ -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) {

View file

@ -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)

View file

@ -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>

View file

@ -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()

View file

@ -68,6 +68,80 @@ 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() {
@ -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);
}