Merge branch 'develop' of github.com:ether/etherpad-lite into support-images

This commit is contained in:
John McLear 2013-12-17 20:32:47 +00:00
commit 3f8e6faec0
30 changed files with 390 additions and 267 deletions

View file

@ -294,15 +294,14 @@ returns the text of a pad formatted as HTML
* `{code: 0, message:"ok", data: {html:"Welcome Text<br>More Text"}}` * `{code: 0, message:"ok", data: {html:"Welcome Text<br>More Text"}}`
* `{code: 1, message:"padID does not exist", data: null}` * `{code: 1, message:"padID does not exist", data: null}`
#### setHTML(padID, text) #### setHTML(padID, html)
* API >= 1 * API >= 1
sets the html of a pad sets the text of a pad based on HTML, HTML must be well formed. Malformed HTML will send a warning to the API log.
*Example returns:* *Example returns:*
* `{code: 0, message:"ok", data: null}` * `{code: 0, message:"ok", data: null}`
* `{code: 1, message:"padID does not exist", data: null}` * `{code: 1, message:"padID does not exist", data: null}`
* `{code: 1, message:"text too long", data: null}`
#### getAttributePool(padID) #### getAttributePool(padID)
* API >= 1.2.8 * API >= 1.2.8

View file

@ -1,8 +1,8 @@
{ {
"@metadata": { "@metadata": {
"authors": { "authors": [
"1": "Xuacu" "Xuacu"
} ]
}, },
"index.newPad": "Nuevu bloc", "index.newPad": "Nuevu bloc",
"index.createOpenPad": "o crear/abrir un bloc col nome:", "index.createOpenPad": "o crear/abrir un bloc col nome:",

View file

@ -1,10 +1,10 @@
{ {
"@metadata": { "@metadata": {
"authors": { "authors": [
"0": "Christian List", "Christian List",
"1": "Peter Alberti", "Peter Alberti",
"3": "Steenth" "Steenth"
} ]
}, },
"index.newPad": "Ny Pad", "index.newPad": "Ny Pad",
"index.createOpenPad": "eller opret/åbn en Pad med navnet:", "index.createOpenPad": "eller opret/åbn en Pad med navnet:",

View file

@ -1,11 +1,11 @@
{ {
"@metadata": { "@metadata": {
"authors": { "authors": [
"0": "Metalhead64", "Metalhead64",
"1": "Mklehr", "Mklehr",
"2": "Nipsky", "Nipsky",
"4": "Wikinaut" "Wikinaut"
} ]
}, },
"index.newPad": "Neues Pad", "index.newPad": "Neues Pad",
"index.createOpenPad": "Pad mit folgendem Namen öffnen:", "index.createOpenPad": "Pad mit folgendem Namen öffnen:",

View file

@ -1,16 +1,16 @@
{ {
"@metadata": { "@metadata": {
"authors": { "authors": [
"0": "Armando-Martin", "Armando-Martin",
"1": "Jacobo", "Jacobo",
"2": "Joker", "Joker",
"3": "Larjona", "Larjona",
"4": "Mklehr", "Mklehr",
"5": "Rubenwap", "Rubenwap",
"7": "VegaDark", "VegaDark",
"8": "Vivaelcelta", "Vivaelcelta",
"9": "Xuacu" "Xuacu"
} ]
}, },
"index.newPad": "Nuevo Pad", "index.newPad": "Nuevo Pad",
"index.createOpenPad": "o crea/abre un Pad con el nombre:", "index.createOpenPad": "o crea/abre un Pad con el nombre:",

View file

@ -1,13 +1,13 @@
{ {
"@metadata": { "@metadata": {
"authors": { "authors": [
"0": "BMRG14", "BMRG14",
"1": "Dalba", "Dalba",
"2": "Ebraminio", "Ebraminio",
"3": "Reza1615", "Reza1615",
"5": "ZxxZxxZ", "ZxxZxxZ",
"6": "الناز" "الناز"
} ]
}, },
"index.newPad": "دفترچه یادداشت تازه", "index.newPad": "دفترچه یادداشت تازه",
"index.createOpenPad": "یا ایجاد/بازکردن یک دفترچه یادداشت با نام:", "index.createOpenPad": "یا ایجاد/بازکردن یک دفترچه یادداشت با نام:",

View file

@ -1,15 +1,16 @@
{ {
"@metadata": { "@metadata": {
"authors": { "authors": [
"0": "Artnay", "Artnay",
"1": "Jl", "Jl",
"2": "Lliehu", "Lliehu",
"3": "Nedergard", "Nedergard",
"4": "Nike", "Nike",
"6": "Stryn", "Stryn",
"7": "Veikk0.ma", "Tomi Toivio",
"8": "VezonThunder" "Veikk0.ma",
} "VezonThunder"
]
}, },
"index.newPad": "Uusi muistio", "index.newPad": "Uusi muistio",
"index.createOpenPad": "tai luo tai avaa muistio nimellä:", "index.createOpenPad": "tai luo tai avaa muistio nimellä:",
@ -19,8 +20,8 @@
"pad.toolbar.strikethrough.title": "Yliviivaus", "pad.toolbar.strikethrough.title": "Yliviivaus",
"pad.toolbar.ol.title": "Numeroitu lista", "pad.toolbar.ol.title": "Numeroitu lista",
"pad.toolbar.ul.title": "Numeroimaton lista", "pad.toolbar.ul.title": "Numeroimaton lista",
"pad.toolbar.indent.title": "Sisennä", "pad.toolbar.indent.title": "Sisennä (TAB)",
"pad.toolbar.unindent.title": "Ulonna", "pad.toolbar.unindent.title": "Ulonna (Shift+TAB)",
"pad.toolbar.undo.title": "Kumoa (Ctrl-Z)", "pad.toolbar.undo.title": "Kumoa (Ctrl-Z)",
"pad.toolbar.redo.title": "Tee uudelleen (Ctrl-Y)", "pad.toolbar.redo.title": "Tee uudelleen (Ctrl-Y)",
"pad.toolbar.clearAuthorship.title": "Poista kirjoittajavärit", "pad.toolbar.clearAuthorship.title": "Poista kirjoittajavärit",

View file

@ -1,22 +1,22 @@
{ {
"@metadata": { "@metadata": {
"authors": { "authors": [
"0": "Cquoi", "Cquoi",
"1": "Crochet.david", "Crochet.david",
"2": "Gomoko", "Gomoko",
"3": "Goofy", "Goofy",
"4": "Goofy-bz", "Goofy-bz",
"5": "Jean-Frédéric", "Jean-Frédéric",
"6": "Leviathan", "Leviathan",
"7": "McDutchie", "McDutchie",
"8": "Metroitendo", "Metroitendo",
"9": "Od1n", "Od1n",
"10": "Peter17", "Peter17",
"11": "Quenenni", "Quenenni",
"12": "Rastus Vernon", "Rastus Vernon",
"14": "Stephane Cottin", "Stephane Cottin",
"15": "Tux-tn" "Tux-tn"
} ]
}, },
"index.newPad": "Nouveau pad", "index.newPad": "Nouveau pad",
"index.createOpenPad": "ou créer/ouvrir un pad intitulé :", "index.createOpenPad": "ou créer/ouvrir un pad intitulé :",

View file

@ -1,8 +1,8 @@
{ {
"@metadata": { "@metadata": {
"authors": { "authors": [
"1": "Toliño" "Toliño"
} ]
}, },
"index.newPad": "Novo documento", "index.newPad": "Novo documento",
"index.createOpenPad": "ou cree/abra un documento co nome:", "index.createOpenPad": "ou cree/abra un documento co nome:",

View file

@ -1,11 +1,11 @@
{ {
"@metadata": { "@metadata": {
"authors": { "authors": [
"0": "Amire80", "Amire80",
"1": "Ofrahod", "Ofrahod",
"3": "YaronSh", "YaronSh",
"4": "תומר ט" "תומר ט"
} ]
}, },
"index.newPad": "פנקס חדש", "index.newPad": "פנקס חדש",
"index.createOpenPad": "ליצור או לפתוח פנקס בשם:", "index.createOpenPad": "ליצור או לפתוח פנקס בשם:",

View file

@ -1,11 +1,11 @@
{ {
"@metadata": { "@metadata": {
"authors": { "authors": [
"0": "Dj", "Dj",
"1": "Misibacsi", "Misibacsi",
"2": "R-Joe", "R-Joe",
"4": "Tgr" "Tgr"
} ]
}, },
"index.newPad": "Új notesz", "index.newPad": "Új notesz",
"index.createOpenPad": "vagy notesz létrehozása ezen a néven:", "index.createOpenPad": "vagy notesz létrehozása ezen a néven:",

View file

@ -1,11 +1,11 @@
{ {
"@metadata": { "@metadata": {
"authors": { "authors": [
"0": "Beta16", "Beta16",
"1": "Gianfranco", "Gianfranco",
"2": "Muxator", "Muxator",
"4": "Vituzzu" "Vituzzu"
} ]
}, },
"index.newPad": "Nuovo Pad", "index.newPad": "Nuovo Pad",
"index.createOpenPad": "o creare o aprire un Pad con il nome:", "index.createOpenPad": "o creare o aprire un Pad con il nome:",

View file

@ -1,9 +1,9 @@
{ {
"@metadata": { "@metadata": {
"authors": { "authors": [
"0": "Hym411", "Hym411",
"2": "아라" "아라"
} ]
}, },
"index.newPad": "새 패드", "index.newPad": "새 패드",
"index.createOpenPad": "또는 다음 이름으로 패드 만들기/열기:", "index.createOpenPad": "또는 다음 이름으로 패드 만들기/열기:",

View file

@ -1,8 +1,8 @@
{ {
"@metadata": { "@metadata": {
"authors": { "authors": [
"1": "Purodha" "Purodha"
} ]
}, },
"index.newPad": "Neu Padd", "index.newPad": "Neu Padd",
"index.createOpenPad": "udder maach e Padd op med däm Naame:", "index.createOpenPad": "udder maach e Padd op med däm Naame:",

View file

@ -1,9 +1,9 @@
{ {
"@metadata": { "@metadata": {
"authors": { "authors": [
"0": "Robby", "Robby",
"2": "Soued031" "Soued031"
} ]
}, },
"index.newPad": "Neie Pad", "index.newPad": "Neie Pad",
"pad.toolbar.ol.title": "Numeréiert Lëscht", "pad.toolbar.ol.title": "Numeréiert Lëscht",

View file

@ -12,8 +12,8 @@
"pad.toolbar.strikethrough.title": "Garis lorek", "pad.toolbar.strikethrough.title": "Garis lorek",
"pad.toolbar.ol.title": "Senarai tertib", "pad.toolbar.ol.title": "Senarai tertib",
"pad.toolbar.ul.title": "Senarai tak tertib", "pad.toolbar.ul.title": "Senarai tak tertib",
"pad.toolbar.indent.title": "Engsot ke dalam", "pad.toolbar.indent.title": "Engsot ke dalam (TAB)",
"pad.toolbar.unindent.title": "Engsot ke luar", "pad.toolbar.unindent.title": "Engsot ke luar (Shift + TAB)",
"pad.toolbar.undo.title": "Buat asal (Ctrl-Z)", "pad.toolbar.undo.title": "Buat asal (Ctrl-Z)",
"pad.toolbar.redo.title": "Buat semula (Ctrl-Y)", "pad.toolbar.redo.title": "Buat semula (Ctrl-Y)",
"pad.toolbar.clearAuthorship.title": "Padamkan Warna Pengarang", "pad.toolbar.clearAuthorship.title": "Padamkan Warna Pengarang",
@ -66,11 +66,15 @@
"pad.modals.initsocketfail.cause": "Ini mungkin disebabkan oleh masalah dengan pelayar atau sambungan internet anda.", "pad.modals.initsocketfail.cause": "Ini mungkin disebabkan oleh masalah dengan pelayar atau sambungan internet anda.",
"pad.modals.slowcommit.explanation": "Pelayan tidak membalas.", "pad.modals.slowcommit.explanation": "Pelayan tidak membalas.",
"pad.modals.slowcommit.cause": "Ini mungkin disebabkan oleh masalah dengan kesambungan rangkaian anda.", "pad.modals.slowcommit.cause": "Ini mungkin disebabkan oleh masalah dengan kesambungan rangkaian anda.",
"pad.modals.badChangeset.explanation": "Suntingan yang telah anda lakukan telah dikira sebagai terlarang oleh pelayan penyegerakan.",
"pad.modals.badChangeset.cause": "Ini mungkin disebabkan oleh konfigurasi pelayan salah atau sesuatu kelakuan yang tidak dijangka. Sila hubungi penyelia servis anda jika anda merasakan ini adalah satu kesilapan. Cuba sambungkan semula talian untuk terus menyuntung.",
"pad.modals.corruptPad.explanation": "Pad yang anda cuba akses itu telah tercemar.",
"pad.modals.corruptPad.cause": "Ini mungkin disebabkan oleh konfigurasi pelayan salah atau sesuatu kelakuan yang tidak dijangka. Sila hubungi penyelia servis anda.",
"pad.modals.deleted": "Dihapuskan.", "pad.modals.deleted": "Dihapuskan.",
"pad.modals.deleted.explanation": "Pad ini telah dibuang.", "pad.modals.deleted.explanation": "Pad ini telah dibuang.",
"pad.modals.disconnected": "Sambungan anda telah diputuskan.", "pad.modals.disconnected": "Sambungan anda telah diputuskan.",
"pad.modals.disconnected.explanation": "Sambungan ke pelayan terputus", "pad.modals.disconnected.explanation": "Sambungan ke pelayan terputus",
"pad.modals.disconnected.cause": "Pelayan mungkin tidak dapat dicapai. Sila beritahu kami jika masalah ini berterusan.", "pad.modals.disconnected.cause": "Pelayan mungkin tidak dapat dicapai. Sila beritahu penyelia servis jika masalah ini berterusan.",
"pad.share": "Kongsikan pad ini", "pad.share": "Kongsikan pad ini",
"pad.share.readonly": "Baca sahaja", "pad.share.readonly": "Baca sahaja",
"pad.share.link": "Pautan", "pad.share.link": "Pautan",

86
src/locales/ne.json Normal file
View file

@ -0,0 +1,86 @@
{
"@metadata": {
"authors": [
"सरोज कुमार ढकाल"
]
},
"index.newPad": "नयाँ प्याड",
"index.createOpenPad": "नाम सहितको नयाँ प्याड सिर्जना गर्ने / खोल्ने :",
"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.ul.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.cancel": "रद्द",
"pad.loading": "लोड हुदैछ...",
"pad.passwordRequired": "यो प्यड खोल्न पासवर्ड चाहिन्छ",
"pad.permissionDenied": "तपाईँलाई यस प्याड खोल्न अनुमति छैन",
"pad.wrongPassword": "तपाईँको पासवर्ड गलत थियो",
"pad.settings.padSettings": "प्याड सेटिङ्गहरू",
"pad.settings.myView": "मेरो दृष्य",
"pad.settings.stickychat": "पर्दामा सधै च्याट गर्ने",
"pad.settings.colorcheck": "लेखकीय रङ्ग",
"pad.settings.linenocheck": "हरफ संख्या",
"pad.settings.rtlcheck": "के सामग्री दाहिने देखि देब्रे पढ्ने हो ?",
"pad.settings.fontType": "फन्ट प्रकार:",
"pad.settings.fontType.normal": "सामान्य",
"pad.settings.fontType.monospaced": "मोनोस्पेस",
"pad.settings.globalView": "विश्वव्यापी दृष्य",
"pad.settings.language": "भाषा:",
"pad.importExport.import_export": "आयात/निर्यात",
"pad.importExport.import": "कुनै पनि पाठ रहेको फाइल या कागजात अपलोड गर्नुहोस्",
"pad.importExport.importSuccessful": "सफल भयो!",
"pad.importExport.export": "निम्न रुपमा प्याड निर्यात गर्ने :",
"pad.importExport.exporthtml": "HTML",
"pad.importExport.exportplain": "साधारण पाठ",
"pad.importExport.exportword": "माइक्रोसफ्ट वर्ड",
"pad.importExport.exportpdf": "पिडिएफ",
"pad.importExport.exportopen": "ओडिएफ(खुल्ला कागजात ढाँचा)",
"pad.importExport.exportdokuwiki": "डकुविकि",
"pad.modals.connected": "जोडीएको।",
"pad.modals.reconnecting": "तपाईँको प्याडमा पुन: जडान गर्दै",
"pad.modals.deleted": "मेटिएको ।",
"pad.modals.deleted.explanation": "यो प्याड हटाइसकेको छ ।",
"pad.modals.disconnected": "तपाईँको जडान अवरुद्ध भयो ।",
"pad.modals.disconnected.explanation": "तपाईँको सर्भरसँगको जडान अवरुद्ध भयो",
"pad.share": "यस प्यडलाई बाड्ने",
"pad.share.readonly": "पढ्ने मात्र",
"pad.share.link": "लिङ्क",
"pad.chat": "कुराकानी",
"pad.chat.title": "यस प्याडको लागि कुराकानी खोल्ने",
"pad.chat.loadmessages": "थप सन्देशहरू खोल्ने",
"timeslider.toolbar.returnbutton": "प्याडमा फर्कनुहोस्",
"timeslider.toolbar.authors": "लेखकहरु:",
"timeslider.toolbar.authorsList": "कुनै पनि लेखकहरू छैनन्",
"timeslider.toolbar.exportlink.title": "निर्यात",
"timeslider.exportCurrent": "हालको संस्करण निम्म रुपमा निर्यात गर्ने :",
"timeslider.version": "संस्करण {{version}}",
"timeslider.saved": "सङ्ग्रह गरिएको {{month}} {{day}}, {{year}}",
"timeslider.dateformat": "{{month}}/{{day}}/{{year}} {{hours}}:{{minutes}}:{{seconds}}",
"timeslider.month.january": "जनवरी",
"timeslider.month.february": "फेब्रुअरी",
"timeslider.month.march": "मार्च",
"timeslider.month.april": "एप्रील",
"timeslider.month.may": "मे",
"timeslider.month.june": "जुन",
"timeslider.month.july": "जुलाई",
"timeslider.month.august": "अगस्ट",
"timeslider.month.september": "सेप्टेम्बर",
"timeslider.month.october": "अक्टोबर",
"timeslider.month.november": "नोभेम्बर",
"timeslider.month.december": "डिसेम्बर",
"timeslider.unnamedauthors": "{{num}} unnamed {[plural(num) one: author, other: authors ]}",
"pad.savedrevs.marked": "यस संस्करणलाई संग्रहितको रुपमा चिनो लगाइएको छैन",
"pad.userlist.entername": "तपाईँको नाम लेख्नुहोस्",
"pad.userlist.unnamed": "नाम नखुलाइएको",
"pad.userlist.guest": "पाहुना",
"pad.userlist.deny": "अस्वीकार गर्ने",
"pad.userlist.approve": "स्वीकृत गर्ने"
}

View file

@ -1,8 +1,8 @@
{ {
"@metadata": { "@metadata": {
"authors": { "authors": [
"1": "Unhammer" "Unhammer"
} ]
}, },
"index.newPad": "Ny blokk", "index.newPad": "Ny blokk",
"index.createOpenPad": "eller opprett/opna ei blokk med namnet:", "index.createOpenPad": "eller opprett/opna ei blokk med namnet:",

View file

@ -1,11 +1,11 @@
{ {
"@metadata": { "@metadata": {
"authors": { "authors": [
"0": "Rezonansowy", "Rezonansowy",
"2": "Ty221", "Ty221",
"3": "WTM", "WTM",
"4": "Woytecr" "Woytecr"
} ]
}, },
"index.newPad": "Nowy dokument", "index.newPad": "Nowy dokument",
"index.createOpenPad": "lub stwórz/otwórz dokument o nazwie:", "index.createOpenPad": "lub stwórz/otwórz dokument o nazwie:",
@ -15,8 +15,8 @@
"pad.toolbar.strikethrough.title": "Przekreślenie", "pad.toolbar.strikethrough.title": "Przekreślenie",
"pad.toolbar.ol.title": "Lista uporządkowana", "pad.toolbar.ol.title": "Lista uporządkowana",
"pad.toolbar.ul.title": "Lista nieuporządkowana", "pad.toolbar.ul.title": "Lista nieuporządkowana",
"pad.toolbar.indent.title": "Wcięcie", "pad.toolbar.indent.title": "Wcięcie (TAB)",
"pad.toolbar.unindent.title": "Zmniejsz wcięcie", "pad.toolbar.unindent.title": "Wcięcie (Shift + TAB)",
"pad.toolbar.undo.title": "Cofnij (Ctrl-Z)", "pad.toolbar.undo.title": "Cofnij (Ctrl-Z)",
"pad.toolbar.redo.title": "Ponów (Ctrl-Y)", "pad.toolbar.redo.title": "Ponów (Ctrl-Y)",
"pad.toolbar.clearAuthorship.title": "Usuń kolory autorów", "pad.toolbar.clearAuthorship.title": "Usuń kolory autorów",

View file

@ -1,11 +1,11 @@
{ {
"@metadata": { "@metadata": {
"authors": { "authors": [
"0": "Hamilton Abreu", "Hamilton Abreu",
"1": "Luckas", "Luckas",
"3": "Tuliouel", "Tuliouel",
"4": "Waldir" "Waldir"
} ]
}, },
"index.newPad": "Nova Nota", "index.newPad": "Nova Nota",
"index.createOpenPad": "ou crie/abra uma Nota com o nome:", "index.createOpenPad": "ou crie/abra uma Nota com o nome:",

View file

@ -1,9 +1,9 @@
{ {
"@metadata": { "@metadata": {
"authors": { "authors": [
"0": "Lokal Profil", "Lokal Profil",
"2": "WikiPhoenix" "WikiPhoenix"
} ]
}, },
"index.newPad": "Nytt block", "index.newPad": "Nytt block",
"index.createOpenPad": "eller skapa/öppna ett block med namnet:", "index.createOpenPad": "eller skapa/öppna ett block med namnet:",

View file

@ -1,10 +1,10 @@
{ {
"@metadata": { "@metadata": {
"authors": { "authors": [
"0": "JVRKPRASAD", "JVRKPRASAD",
"1": "Malkum", "Malkum",
"3": "Veeven" "Veeven"
} ]
}, },
"index.newPad": "కొత్త పలక", "index.newPad": "కొత్త పలక",
"index.createOpenPad": "ఒక పేరుతో పలకని సృష్టించండి లేదా అదే పేరుతో ఉన్న పలకని తెరవండి", "index.createOpenPad": "ఒక పేరుతో పలకని సృష్టించండి లేదా అదే పేరుతో ఉన్న పలకని తెరవండి",

View file

@ -3,6 +3,7 @@
"authors": [ "authors": [
"Emperyan", "Emperyan",
"Erdemaslancan", "Erdemaslancan",
"Joseph",
"Meelo" "Meelo"
] ]
}, },
@ -14,8 +15,8 @@
"pad.toolbar.strikethrough.title": "Üstü Çizili", "pad.toolbar.strikethrough.title": "Üstü Çizili",
"pad.toolbar.ol.title": "Sıralı liste", "pad.toolbar.ol.title": "Sıralı liste",
"pad.toolbar.ul.title": "Sırasız Liste", "pad.toolbar.ul.title": "Sırasız Liste",
"pad.toolbar.indent.title": "Girintiyi arttır", "pad.toolbar.indent.title": "Girintiyi arttır (TAB)",
"pad.toolbar.unindent.title": "Girintiyi azalt", "pad.toolbar.unindent.title": "Girintiyi azalt (Shift+TAB)",
"pad.toolbar.undo.title": "Geri Al (Ctrl-Z)", "pad.toolbar.undo.title": "Geri Al (Ctrl-Z)",
"pad.toolbar.redo.title": "Yenile (Ctrl-Y)", "pad.toolbar.redo.title": "Yenile (Ctrl-Y)",
"pad.toolbar.clearAuthorship.title": "Yazarlık Renklerini Temizle", "pad.toolbar.clearAuthorship.title": "Yazarlık Renklerini Temizle",

View file

@ -1,12 +1,12 @@
{ {
"@metadata": { "@metadata": {
"authors": { "authors": [
"0": "Andriykopanytsia", "Andriykopanytsia",
"1": "Base", "Base",
"2": "Olvin", "Olvin",
"4": "Steve.rusyn", "Steve.rusyn",
"5": "SteveR" "SteveR"
} ]
}, },
"index.newPad": "Створити", "index.newPad": "Створити",
"index.createOpenPad": "або створити/відкрити документ з назвою:", "index.createOpenPad": "або створити/відкрити документ з назвою:",

View file

@ -21,8 +21,8 @@
"pad.toolbar.strikethrough.title": "删除线", "pad.toolbar.strikethrough.title": "删除线",
"pad.toolbar.ol.title": "有序列表", "pad.toolbar.ol.title": "有序列表",
"pad.toolbar.ul.title": "无序列表", "pad.toolbar.ul.title": "无序列表",
"pad.toolbar.indent.title": "增加缩进", "pad.toolbar.indent.title": "增加缩进TAB",
"pad.toolbar.unindent.title": "减少缩进", "pad.toolbar.unindent.title": "减少缩进Shift+TAB",
"pad.toolbar.undo.title": "撤消 (Ctrl-Z)", "pad.toolbar.undo.title": "撤消 (Ctrl-Z)",
"pad.toolbar.redo.title": "重做 (Ctrl-Y)", "pad.toolbar.redo.title": "重做 (Ctrl-Y)",
"pad.toolbar.clearAuthorship.title": "清除作者颜色", "pad.toolbar.clearAuthorship.title": "清除作者颜色",

View file

@ -1,12 +1,12 @@
{ {
"@metadata": { "@metadata": {
"authors": { "authors": [
"0": "Justincheng12345", "Justincheng12345",
"1": "Liuxinyu970226", "Liuxinyu970226",
"2": "Shangkuanlc", "Shangkuanlc",
"3": "Shirayuki", "Shirayuki",
"5": "Simon Shek" "Simon Shek"
} ]
}, },
"index.newPad": "新Pad", "index.newPad": "新Pad",
"index.createOpenPad": "或創建開啟以下名稱的pad", "index.createOpenPad": "或創建開啟以下名稱的pad",

View file

@ -361,6 +361,8 @@ exports.getHTML = function(padID, rev, callback)
exportHtml.getPadHTML(pad, rev, function(err, html) exportHtml.getPadHTML(pad, rev, function(err, html)
{ {
if(ERR(err, callback)) return; if(ERR(err, callback)) return;
html = "<!DOCTYPE HTML><html><body>" +html; // adds HTML head
html += "</body></html>";
data = {html: html}; data = {html: html};
callback(null, data); callback(null, data);
}); });
@ -371,6 +373,8 @@ exports.getHTML = function(padID, rev, callback)
exportHtml.getPadHTML(pad, undefined, function (err, html) exportHtml.getPadHTML(pad, undefined, function (err, html)
{ {
if(ERR(err, callback)) return; if(ERR(err, callback)) return;
html = "<!DOCTYPE HTML><html><body>" +html; // adds HTML head
html += "</body></html>";
data = {html: html}; data = {html: html};
callback(null, data); callback(null, data);
}); });
@ -378,15 +382,30 @@ exports.getHTML = function(padID, rev, callback)
}); });
} }
/**
setHTML(padID, html) sets the text of a pad based on HTML
Example returns:
{code: 0, message:"ok", data: null}
{code: 1, message:"padID does not exist", data: null}
*/
exports.setHTML = function(padID, html, callback) exports.setHTML = function(padID, html, callback)
{ {
//html is required
if(typeof html != "string")
{
callback(new customError("html is no string","apierror"));
return;
}
//get the pad //get the pad
getPadSafe(padID, true, function(err, pad) getPadSafe(padID, true, function(err, pad)
{ {
if(ERR(err, callback)) return; if(ERR(err, callback)) return;
// add a new changeset with the new html to the pad // add a new changeset with the new html to the pad
importHtml.setPadHTML(pad, cleanText(html)); importHtml.setPadHTML(pad, cleanText(html), callback);
//update the clients on the pad //update the clients on the pad
padMessageHandler.updatePadClients(pad, callback); padMessageHandler.updatePadClients(pad, callback);

View file

@ -203,7 +203,11 @@ Pad.prototype.getInternalRevisionAText = function getInternalRevisionAText(targe
{ {
curRev++; curRev++;
var cs = changesets[curRev]; var cs = changesets[curRev];
atext = Changeset.applyToAText(cs, atext, apool); try{
atext = Changeset.applyToAText(cs, atext, apool);
}catch(e) {
return callback(e)
}
} }
callback(null); callback(null);

View file

@ -1,6 +1,6 @@
/** /**
* The MessageHandler handles all Messages that comes from Socket.IO and controls the sessions * The MessageHandler handles all Messages that comes from Socket.IO and controls the sessions
*/ */
/* /*
* Copyright 2009 Google Inc., 2011 Peter 'Pita' Martischka (Primary Technology Ltd) * Copyright 2009 Google Inc., 2011 Peter 'Pita' Martischka (Primary Technology Ltd)
@ -106,12 +106,12 @@ exports.kickSessionsFromPad = function(padID)
* @param client the client that leaves * @param client the client that leaves
*/ */
exports.handleDisconnect = function(client) exports.handleDisconnect = function(client)
{ {
stats.meter('disconnects').mark(); stats.meter('disconnects').mark();
//save the padname of this session //save the padname of this session
var session = sessioninfos[client.id]; var session = sessioninfos[client.id];
//if this connection was already etablished with a handshake, send a disconnect message to the others //if this connection was already etablished with a handshake, send a disconnect message to the others
if(session && session.author) if(session && session.author)
{ {
@ -128,7 +128,7 @@ exports.handleDisconnect = function(client)
authorManager.getAuthorColorId(session.author, function(err, color) authorManager.getAuthorColorId(session.author, function(err, color)
{ {
ERR(err); ERR(err);
//prepare the notification for the other users on the pad, that this user left //prepare the notification for the other users on the pad, that this user left
var messageToTheOtherUsers = { var messageToTheOtherUsers = {
"type": "COLLABROOM", "type": "COLLABROOM",
@ -142,14 +142,14 @@ exports.handleDisconnect = function(client)
} }
} }
}; };
//Go trough all user that are still on the pad, and send them the USER_LEAVE message //Go trough all user that are still on the pad, and send them the USER_LEAVE message
client.broadcast.to(session.padId).json.send(messageToTheOtherUsers); client.broadcast.to(session.padId).json.send(messageToTheOtherUsers);
}); });
} }
//Delete the sessioninfos entrys of this session //Delete the sessioninfos entrys of this session
delete sessioninfos[client.id]; delete sessioninfos[client.id];
} }
/** /**
@ -158,7 +158,7 @@ exports.handleDisconnect = function(client)
* @param message the message from the client * @param message the message from the client
*/ */
exports.handleMessage = function(client, message) exports.handleMessage = function(client, message)
{ {
if(message == null) if(message == null)
{ {
return; return;
@ -174,7 +174,7 @@ exports.handleMessage = function(client, message)
var handleMessageHook = function(callback){ var handleMessageHook = function(callback){
var dropMessage = false; var dropMessage = false;
// Call handleMessage hook. If a plugin returns null, the message will be dropped. Note that for all messages // Call handleMessage hook. If a plugin returns null, the message will be dropped. Note that for all messages
// handleMessage will be called, even if the client is not authorized // handleMessage will be called, even if the client is not authorized
hooks.aCallAll("handleMessage", { client: client, message: message }, function ( err, messages ) { hooks.aCallAll("handleMessage", { client: client, message: message }, function ( err, messages ) {
if(ERR(err, callback)) return; if(ERR(err, callback)) return;
@ -183,7 +183,7 @@ exports.handleMessage = function(client, message)
dropMessage = true; dropMessage = true;
} }
}); });
// If no plugins explicitly told us to drop the message, its ok to proceed // If no plugins explicitly told us to drop the message, its ok to proceed
if(!dropMessage){ callback() }; if(!dropMessage){ callback() };
}); });
@ -259,7 +259,7 @@ exports.handleMessage = function(client, message)
var checkAccessCallback = function(err, statusObject) var checkAccessCallback = function(err, statusObject)
{ {
if(ERR(err, callback)) return; if(ERR(err, callback)) return;
//access was granted //access was granted
if(statusObject.accessStatus == "grant") if(statusObject.accessStatus == "grant")
{ {
@ -297,17 +297,17 @@ exports.handleMessage = function(client, message)
function handleSaveRevisionMessage(client, message){ function handleSaveRevisionMessage(client, message){
var padId = sessioninfos[client.id].padId; var padId = sessioninfos[client.id].padId;
var userId = sessioninfos[client.id].author; var userId = sessioninfos[client.id].author;
padManager.getPad(padId, function(err, pad) padManager.getPad(padId, function(err, pad)
{ {
if(ERR(err)) return; if(ERR(err)) return;
pad.addSavedRevision(pad.head, userId); pad.addSavedRevision(pad.head, userId);
}); });
} }
/** /**
* Handles a custom message, different to the function below as it handles objects not strings and you can * Handles a custom message, different to the function below as it handles objects not strings and you can
* direct the message to specific sessionID * direct the message to specific sessionID
* *
* @param msg {Object} the message we're sending * @param msg {Object} the message we're sending
@ -356,10 +356,10 @@ function handleChatMessage(client, message)
var userId = sessioninfos[client.id].author; var userId = sessioninfos[client.id].author;
var text = message.data.text; var text = message.data.text;
var padId = sessioninfos[client.id].padId; var padId = sessioninfos[client.id].padId;
var pad; var pad;
var userName; var userName;
async.series([ async.series([
//get the pad //get the pad
function(callback) function(callback)
@ -385,7 +385,7 @@ function handleChatMessage(client, message)
{ {
//save the chat message //save the chat message
pad.appendChatMessage(text, userId, time); pad.appendChatMessage(text, userId, time);
var msg = { var msg = {
type: "COLLABROOM", type: "COLLABROOM",
data: { data: {
@ -396,10 +396,10 @@ function handleChatMessage(client, message)
text: text text: text
} }
}; };
//broadcast the chat message to everyone on the pad //broadcast the chat message to everyone on the pad
socketio.sockets.in(padId).json.send(msg); socketio.sockets.in(padId).json.send(msg);
callback(); callback();
} }
], function(err) ], function(err)
@ -425,20 +425,20 @@ function handleGetChatMessages(client, message)
messageLogger.warn("Dropped message, GetChatMessages Message has no start!"); messageLogger.warn("Dropped message, GetChatMessages Message has no start!");
return; return;
} }
var start = message.data.start; var start = message.data.start;
var end = message.data.end; var end = message.data.end;
var count = start - count; var count = start - count;
if(count < 0 && count > 100) if(count < 0 && count > 100)
{ {
messageLogger.warn("Dropped message, GetChatMessages Message, client requested invalid amout of messages!"); messageLogger.warn("Dropped message, GetChatMessages Message, client requested invalid amout of messages!");
return; return;
} }
var padId = sessioninfos[client.id].padId; var padId = sessioninfos[client.id].padId;
var pad; var pad;
async.series([ async.series([
//get the pad //get the pad
function(callback) function(callback)
@ -488,10 +488,10 @@ function handleSuggestUserName(client, message)
messageLogger.warn("Dropped message, suggestUserName Message has no unnamedId!"); messageLogger.warn("Dropped message, suggestUserName Message has no unnamedId!");
return; return;
} }
var padId = sessioninfos[client.id].padId, var padId = sessioninfos[client.id].padId,
clients = socketio.sockets.clients(padId); clients = socketio.sockets.clients(padId);
//search the author and send him this message //search the author and send him this message
for(var i = 0; i < clients.length; i++) { for(var i = 0; i < clients.length; i++) {
var session = sessioninfos[clients[i].id]; var session = sessioninfos[clients[i].id];
@ -520,14 +520,14 @@ function handleUserInfoUpdate(client, message)
messageLogger.warn("Dropped message, USERINFO_UPDATE Message has no colorId!"); messageLogger.warn("Dropped message, USERINFO_UPDATE Message has no colorId!");
return; return;
} }
//Find out the author name of this session //Find out the author name of this session
var author = sessioninfos[client.id].author; var author = sessioninfos[client.id].author;
//Tell the authorManager about the new attributes //Tell the authorManager about the new attributes
authorManager.setAuthorColorId(author, message.data.userInfo.colorId); authorManager.setAuthorColorId(author, message.data.userInfo.colorId);
authorManager.setAuthorName(author, message.data.userInfo.name); authorManager.setAuthorName(author, message.data.userInfo.name);
var padId = sessioninfos[client.id].padId; var padId = sessioninfos[client.id].padId;
var infoMsg = { var infoMsg = {
@ -545,7 +545,7 @@ function handleUserInfoUpdate(client, message)
} }
} }
}; };
//Send the other clients on the pad the update message //Send the other clients on the pad the update message
client.broadcast.to(padId).json.send(infoMsg); client.broadcast.to(padId).json.send(infoMsg);
} }
@ -588,7 +588,7 @@ function handleUserChanges(data, cb)
messageLogger.warn("Dropped message, USER_CHANGES Message has no changeset!"); messageLogger.warn("Dropped message, USER_CHANGES Message has no changeset!");
return cb(); return cb();
} }
//get all Vars we need //get all Vars we need
var baseRev = message.data.baseRev; var baseRev = message.data.baseRev;
var wireApool = (new AttributePool()).fromJsonable(message.data.apool); var wireApool = (new AttributePool()).fromJsonable(message.data.apool);
@ -596,12 +596,12 @@ function handleUserChanges(data, cb)
// The client might disconnect between our callbacks. We should still // The client might disconnect between our callbacks. We should still
// finish processing the changeset, so keep a reference to the session. // finish processing the changeset, so keep a reference to the session.
var thisSession = sessioninfos[client.id]; var thisSession = sessioninfos[client.id];
var r, apool, pad; var r, apool, pad;
// Measure time to process edit // Measure time to process edit
var stopWatch = stats.timer('edits').start(); var stopWatch = stats.timer('edits').start();
async.series([ async.series([
//get the pad //get the pad
function(callback) function(callback)
@ -617,7 +617,7 @@ function handleUserChanges(data, cb)
function(callback) function(callback)
{ {
//ex. _checkChangesetAndPool //ex. _checkChangesetAndPool
try try
{ {
// Verify that the changeset has valid syntax and is in canonical form // Verify that the changeset has valid syntax and is in canonical form
@ -644,9 +644,9 @@ function handleUserChanges(data, cb)
if('author' == attr[0] && attr[1] != thisSession.author) throw new Error("Trying to submit changes as another author in changeset "+changeset); if('author' == attr[0] && attr[1] != thisSession.author) throw new Error("Trying to submit changes as another author in changeset "+changeset);
}) })
} }
//ex. adoptChangesetAttribs //ex. adoptChangesetAttribs
//Afaik, it copies the new attributes from the changeset, to the global Attribute Pool //Afaik, it copies the new attributes from the changeset, to the global Attribute Pool
changeset = Changeset.moveOpsToNewPool(changeset, wireApool, pad.pool); changeset = Changeset.moveOpsToNewPool(changeset, wireApool, pad.pool);
} }
@ -657,7 +657,7 @@ function handleUserChanges(data, cb)
stats.meter('failedChangesets').mark(); stats.meter('failedChangesets').mark();
return callback(new Error("Can't apply USER_CHANGES, because "+e.message)); return callback(new Error("Can't apply USER_CHANGES, because "+e.message));
} }
//ex. applyUserChanges //ex. applyUserChanges
apool = pad.pool; apool = pad.pool;
r = baseRev; r = baseRev;
@ -671,7 +671,7 @@ function handleUserChanges(data, cb)
function(callback) function(callback)
{ {
r++; r++;
pad.getRevisionChangeset(r, function(err, c) pad.getRevisionChangeset(r, function(err, c)
{ {
if(ERR(err, callback)) return; if(ERR(err, callback)) return;
@ -704,16 +704,16 @@ function handleUserChanges(data, cb)
function (callback) function (callback)
{ {
var prevText = pad.text(); var prevText = pad.text();
if (Changeset.oldLen(changeset) != prevText.length) if (Changeset.oldLen(changeset) != prevText.length)
{ {
client.json.send({disconnect:"badChangeset"}); client.json.send({disconnect:"badChangeset"});
stats.meter('failedChangesets').mark(); stats.meter('failedChangesets').mark();
return callback(new Error("Can't apply USER_CHANGES "+changeset+" with oldLen " + Changeset.oldLen(changeset) + " to document of length " + prevText.length)); return callback(new Error("Can't apply USER_CHANGES "+changeset+" with oldLen " + Changeset.oldLen(changeset) + " to document of length " + prevText.length));
} }
pad.appendRevision(changeset, thisSession.author); pad.appendRevision(changeset, thisSession.author);
var correctionChangeset = _correctMarkersInPad(pad.atext, pad.pool); var correctionChangeset = _correctMarkersInPad(pad.atext, pad.pool);
if (correctionChangeset) { if (correctionChangeset) {
pad.appendRevision(correctionChangeset); pad.appendRevision(correctionChangeset);
@ -724,7 +724,7 @@ function handleUserChanges(data, cb)
var nlChangeset = Changeset.makeSplice(pad.text(), pad.text().length-1, 0, "\n"); var nlChangeset = Changeset.makeSplice(pad.text(), pad.text().length-1, 0, "\n");
pad.appendRevision(nlChangeset); pad.appendRevision(nlChangeset);
} }
exports.updatePadClients(pad, function(er) { exports.updatePadClients(pad, function(er) {
ERR(er) ERR(er)
}); });
@ -739,16 +739,16 @@ function handleUserChanges(data, cb)
} }
exports.updatePadClients = function(pad, callback) exports.updatePadClients = function(pad, callback)
{ {
//skip this step if noone is on this pad //skip this step if noone is on this pad
var roomClients = socketio.sockets.clients(pad.id); var roomClients = socketio.sockets.clients(pad.id);
if(roomClients.length==0) if(roomClients.length==0)
return callback(); return callback();
// since all clients usually get the same set of changesets, store them in local cache // since all clients usually get the same set of changesets, store them in local cache
// to remove unnecessary roundtrip to the datalayer // to remove unnecessary roundtrip to the datalayer
// TODO: in REAL world, if we're working without datalayer cache, all requests to revisions will be fired // TODO: in REAL world, if we're working without datalayer cache, all requests to revisions will be fired
// BEFORE first result will be landed to our cache object. The solution is to replace parallel processing // BEFORE first result will be landed to our cache object. The solution is to replace parallel processing
// via async.forEach with sequential for() loop. There is no real benefits of running this in parallel, // via async.forEach with sequential for() loop. There is no real benefits of running this in parallel,
// but benefit of reusing cached revision object is HUGE // but benefit of reusing cached revision object is HUGE
var revCache = {}; var revCache = {};
@ -763,7 +763,7 @@ exports.updatePadClients = function(pad, callback)
async.whilst( async.whilst(
function (){ return sessioninfos[sid] && sessioninfos[sid].rev < pad.getHeadRevisionNumber()}, function (){ return sessioninfos[sid] && sessioninfos[sid].rev < pad.getHeadRevisionNumber()},
function(callback) function(callback)
{ {
var r = sessioninfos[sid].rev + 1; var r = sessioninfos[sid].rev + 1;
async.waterfall([ async.waterfall([
@ -772,7 +772,7 @@ exports.updatePadClients = function(pad, callback)
callback(null, revCache[r]); callback(null, revCache[r]);
else else
pad.getRevision(r, callback); pad.getRevision(r, callback);
}, },
function(revision, callback) function(revision, callback)
{ {
revCache[r] = revision; revCache[r] = revision;
@ -800,8 +800,8 @@ exports.updatePadClients = function(pad, callback)
author: author, author: author,
currentTime: currentTime, currentTime: currentTime,
timeDelta: currentTime - sessioninfos[sid].time timeDelta: currentTime - sessioninfos[sid].time
}}; }};
client.json.send(wireMsg); client.json.send(wireMsg);
} }
@ -814,7 +814,7 @@ exports.updatePadClients = function(pad, callback)
}, },
callback callback
); );
},callback); },callback);
} }
/** /**
@ -830,11 +830,11 @@ function _correctMarkersInPad(atext, apool) {
var offset = 0; var offset = 0;
while (iter.hasNext()) { while (iter.hasNext()) {
var op = iter.next(); var op = iter.next();
var hasMarker = _.find(AttributeManager.lineAttributes, function(attribute){ var hasMarker = _.find(AttributeManager.lineAttributes, function(attribute){
return Changeset.opAttributeValue(op, attribute, apool); return Changeset.opAttributeValue(op, attribute, apool);
}) !== undefined; }) !== undefined;
if (hasMarker) { if (hasMarker) {
for(var i=0;i<op.chars;i++) { for(var i=0;i<op.chars;i++) {
if (offset > 0 && text.charAt(offset-1) != '\n') { if (offset > 0 && text.charAt(offset-1) != '\n') {
@ -864,7 +864,7 @@ function _correctMarkersInPad(atext, apool) {
} }
/** /**
* Handles a CLIENT_READY. A CLIENT_READY is the first message from the client to the server. The Client sends his token * Handles a CLIENT_READY. A CLIENT_READY is the first message from the client to the server. The Client sends his token
* and the pad it wants to enter. The Server answers with the inital values (clientVars) of the pad * and the pad it wants to enter. The Server answers with the inital values (clientVars) of the pad
* @param client the client that send this message * @param client the client that send this message
* @param message the message from the client * @param message the message from the client
@ -922,7 +922,7 @@ function handleClientReady(client, message)
securityManager.checkAccess (padIds.padId, message.sessionID, message.token, message.password, function(err, statusObject) securityManager.checkAccess (padIds.padId, message.sessionID, message.token, message.password, function(err, statusObject)
{ {
if(ERR(err, callback)) return; if(ERR(err, callback)) return;
//access was granted //access was granted
if(statusObject.accessStatus == "grant") if(statusObject.accessStatus == "grant")
{ {
@ -935,7 +935,7 @@ function handleClientReady(client, message)
client.json.send({accessStatus: statusObject.accessStatus}) client.json.send({accessStatus: statusObject.accessStatus})
} }
}); });
}, },
//get all authordata of this new user, and load the pad-object from the database //get all authordata of this new user, and load the pad-object from the database
function(callback) function(callback)
{ {
@ -967,7 +967,7 @@ function handleClientReady(client, message)
function(callback) function(callback)
{ {
var authors = pad.getAllAuthors(); var authors = pad.getAllAuthors();
async.parallel([ async.parallel([
//get timestamp of latest revission needed for timeslider //get timestamp of latest revission needed for timeslider
function(callback) function(callback)
@ -993,7 +993,7 @@ function handleClientReady(client, message)
}, callback); }, callback);
} }
], callback); ], callback);
}, },
//glue the clientVars together, send them and tell the other clients that a new one is there //glue the clientVars together, send them and tell the other clients that a new one is there
function(callback) function(callback)
@ -1013,12 +1013,12 @@ function handleClientReady(client, message)
roomClients[i].json.send({disconnect:"userdup"}); roomClients[i].json.send({disconnect:"userdup"});
} }
} }
//Save in sessioninfos that this session belonges to this pad //Save in sessioninfos that this session belonges to this pad
sessioninfos[client.id].padId = padIds.padId; sessioninfos[client.id].padId = padIds.padId;
sessioninfos[client.id].readOnlyPadId = padIds.readOnlyPadId; sessioninfos[client.id].readOnlyPadId = padIds.readOnlyPadId;
sessioninfos[client.id].readonly = padIds.readonly; sessioninfos[client.id].readonly = padIds.readonly;
//Log creation/(re-)entering of a pad //Log creation/(re-)entering of a pad
client.get('remoteAddress', function(er, ip) { client.get('remoteAddress', function(er, ip) {
//Anonymize the IP address if IP logging is disabled //Anonymize the IP address if IP logging is disabled
@ -1056,7 +1056,7 @@ function handleClientReady(client, message)
client.json.send({disconnect:"corruptPad"});// pull the breaks client.json.send({disconnect:"corruptPad"});// pull the breaks
return callback(); return callback();
} }
// Warning: never ever send padIds.padId to the client. If the // Warning: never ever send padIds.padId to the client. If the
// client is read only you would open a security hole 1 swedish // client is read only you would open a security hole 1 swedish
// mile wide... // mile wide...
@ -1085,7 +1085,7 @@ function handleClientReady(client, message)
"padId": message.padId, "padId": message.padId,
"initialTitle": "Pad: " + message.padId, "initialTitle": "Pad: " + message.padId,
"opts": {}, "opts": {},
// tell the client the number of the latest chat-message, which will be // tell the client the number of the latest chat-message, which will be
// used to request the latest 100 chat-messages later (GET_CHAT_MESSAGES) // used to request the latest 100 chat-messages later (GET_CHAT_MESSAGES)
"chatHead": pad.chatHead, "chatHead": pad.chatHead,
"numConnectedUsers": roomClients.length, "numConnectedUsers": roomClients.length,
@ -1093,7 +1093,7 @@ function handleClientReady(client, message)
"readonly": padIds.readonly, "readonly": padIds.readonly,
"serverTimestamp": new Date().getTime(), "serverTimestamp": new Date().getTime(),
"userId": author, "userId": author,
"abiwordAvailable": settings.abiwordAvailable(), "abiwordAvailable": settings.abiwordAvailable(),
"plugins": { "plugins": {
"plugins": plugins.plugins, "plugins": plugins.plugins,
"parts": plugins.parts, "parts": plugins.parts,
@ -1106,18 +1106,18 @@ function handleClientReady(client, message)
{ {
clientVars.userName = authorName; clientVars.userName = authorName;
} }
//call the clientVars-hook so plugins can modify them before they get sent to the client //call the clientVars-hook so plugins can modify them before they get sent to the client
hooks.aCallAll("clientVars", { clientVars: clientVars, pad: pad }, function ( err, messages ) { hooks.aCallAll("clientVars", { clientVars: clientVars, pad: pad }, function ( err, messages ) {
if(ERR(err, callback)) return; if(ERR(err, callback)) return;
_.each(messages, function(newVars) { _.each(messages, function(newVars) {
//combine our old object with the new attributes from the hook //combine our old object with the new attributes from the hook
for(var attr in newVars) { for(var attr in newVars) {
clientVars[attr] = newVars[attr]; clientVars[attr] = newVars[attr];
} }
}); });
//Join the pad and start receiving updates //Join the pad and start receiving updates
client.join(padIds.padId); client.join(padIds.padId);
//Send the clientVars to the Client //Send the clientVars to the Client
@ -1126,9 +1126,9 @@ function handleClientReady(client, message)
sessioninfos[client.id].rev = pad.getHeadRevisionNumber(); sessioninfos[client.id].rev = pad.getHeadRevisionNumber();
}); });
} }
sessioninfos[client.id].author = author; sessioninfos[client.id].author = author;
//prepare the notification for the other users on the pad, that this user joined //prepare the notification for the other users on the pad, that this user joined
var messageToTheOtherUsers = { var messageToTheOtherUsers = {
"type": "COLLABROOM", "type": "COLLABROOM",
@ -1142,7 +1142,7 @@ function handleClientReady(client, message)
} }
} }
}; };
//Add the authorname of this new User, if avaiable //Add the authorname of this new User, if avaiable
if(authorName != null) if(authorName != null)
{ {
@ -1151,7 +1151,7 @@ function handleClientReady(client, message)
// notify all existing users about new user // notify all existing users about new user
client.broadcast.to(padIds.padId).json.send(messageToTheOtherUsers); client.broadcast.to(padIds.padId).json.send(messageToTheOtherUsers);
//Run trough all sessions of this pad //Run trough all sessions of this pad
async.forEach(socketio.sockets.clients(padIds.padId), function(roomClient, callback) async.forEach(socketio.sockets.clients(padIds.padId), function(roomClient, callback)
{ {
@ -1160,9 +1160,9 @@ function handleClientReady(client, message)
//Jump over, if this session is the connection session //Jump over, if this session is the connection session
if(roomClient.id == client.id) if(roomClient.id == client.id)
return callback(); return callback();
//Since sessioninfos might change while being enumerated, check if the //Since sessioninfos might change while being enumerated, check if the
//sessionID is still assigned to a valid session //sessionID is still assigned to a valid session
if(sessioninfos[roomClient.id] !== undefined) if(sessioninfos[roomClient.id] !== undefined)
author = sessioninfos[roomClient.id].author; author = sessioninfos[roomClient.id].author;
@ -1178,7 +1178,7 @@ function handleClientReady(client, message)
callback(null, historicalAuthorData[author]); callback(null, historicalAuthorData[author]);
else else
authorManager.getAuthor(author, callback); authorManager.getAuthor(author, callback);
}, },
function (authorInfo, callback) function (authorInfo, callback)
{ {
//Send the new User a Notification about this other user //Send the new User a Notification about this other user
@ -1207,7 +1207,7 @@ function handleClientReady(client, message)
} }
/** /**
* Handles a request for a rough changeset, the timeslider client needs it * Handles a request for a rough changeset, the timeslider client needs it
*/ */
function handleChangesetRequest(client, message) function handleChangesetRequest(client, message)
{ {
@ -1237,7 +1237,7 @@ function handleChangesetRequest(client, message)
messageLogger.warn("Dropped message, changeset request has no requestID!"); messageLogger.warn("Dropped message, changeset request has no requestID!");
return; return;
} }
var granularity = message.data.granularity; var granularity = message.data.granularity;
var start = message.data.start; var start = message.data.start;
var end = start + (100 * granularity); var end = start + (100 * granularity);
@ -1255,7 +1255,7 @@ function handleChangesetRequest(client, message)
//build the requested rough changesets and send them back //build the requested rough changesets and send them back
getChangesetInfo(padIds.padId, start, end, granularity, function(err, changesetInfo) getChangesetInfo(padIds.padId, start, end, granularity, function(err, changesetInfo)
{ {
ERR(err); if(err) return console.error('Error while handling a changeset request for '+padIds.padId, err, message.data);
var data = changesetInfo; var data = changesetInfo;
data.requestID = message.data.requestID; data.requestID = message.data.requestID;
@ -1281,47 +1281,49 @@ function getChangesetInfo(padId, startNum, endNum, granularity, callback)
var composedChangesets = {}; var composedChangesets = {};
var revisionDate = []; var revisionDate = [];
var lines; var lines;
var head_revision = 0;
async.series([ async.series([
//get the pad from the database //get the pad from the database
function(callback) function(callback)
{ {
padManager.getPad(padId, function(err, _pad) padManager.getPad(padId, function(err, _pad)
{ {
if(ERR(err, callback)) return; if(ERR(err, callback)) return;
pad = _pad; pad = _pad;
head_revision = pad.getHeadRevisionNumber();
callback(); callback();
}); });
}, },
function(callback) function(callback)
{ {
//calculate the last full endnum //calculate the last full endnum
var lastRev = pad.getHeadRevisionNumber(); var lastRev = pad.getHeadRevisionNumber();
if (endNum > lastRev+1) { if (endNum > lastRev+1) {
endNum = lastRev+1; endNum = lastRev+1;
} }
endNum = Math.floor(endNum / granularity)*granularity; endNum = Math.floor(endNum / granularity)*granularity;
var compositesChangesetNeeded = []; var compositesChangesetNeeded = [];
var revTimesNeeded = []; var revTimesNeeded = [];
//figure out which composite Changeset and revTimes we need, to load them in bulk //figure out which composite Changeset and revTimes we need, to load them in bulk
var compositeStart = startNum; var compositeStart = startNum;
while (compositeStart < endNum) while (compositeStart < endNum)
{ {
var compositeEnd = compositeStart + granularity; var compositeEnd = compositeStart + granularity;
//add the composite Changeset we needed //add the composite Changeset we needed
compositesChangesetNeeded.push({start: compositeStart, end: compositeEnd}); compositesChangesetNeeded.push({start: compositeStart, end: compositeEnd});
//add the t1 time we need //add the t1 time we need
revTimesNeeded.push(compositeStart == 0 ? 0 : compositeStart - 1); revTimesNeeded.push(compositeStart == 0 ? 0 : compositeStart - 1);
//add the t2 time we need //add the t2 time we need
revTimesNeeded.push(compositeEnd - 1); revTimesNeeded.push(compositeEnd - 1);
compositeStart += granularity; compositeStart += granularity;
} }
//get all needed db values parallel //get all needed db values parallel
async.parallel([ async.parallel([
function(callback) function(callback)
@ -1358,58 +1360,57 @@ function getChangesetInfo(padId, startNum, endNum, granularity, callback)
if(ERR(err, callback)) return; if(ERR(err, callback)) return;
lines = _lines; lines = _lines;
callback(); callback();
}); });
} }
], callback); ], callback);
}, },
//doesn't know what happens here excatly :/ //doesn't know what happens here excatly :/
function(callback) function(callback)
{ {
var compositeStart = startNum; var compositeStart = startNum;
while (compositeStart < endNum) while (compositeStart < endNum)
{ {
if (compositeStart + granularity > endNum) var compositeEnd = compositeStart + granularity;
if (compositeEnd > endNum || compositeEnd > head_revision)
{ {
break; break;
} }
var compositeEnd = compositeStart + granularity;
var forwards = composedChangesets[compositeStart + "/" + compositeEnd]; var forwards = composedChangesets[compositeStart + "/" + compositeEnd];
var backwards = Changeset.inverse(forwards, lines.textlines, lines.alines, pad.apool()); var backwards = Changeset.inverse(forwards, lines.textlines, lines.alines, pad.apool());
Changeset.mutateAttributionLines(forwards, lines.alines, pad.apool()); Changeset.mutateAttributionLines(forwards, lines.alines, pad.apool());
Changeset.mutateTextLines(forwards, lines.textlines); Changeset.mutateTextLines(forwards, lines.textlines);
var forwards2 = Changeset.moveOpsToNewPool(forwards, pad.apool(), apool); var forwards2 = Changeset.moveOpsToNewPool(forwards, pad.apool(), apool);
var backwards2 = Changeset.moveOpsToNewPool(backwards, pad.apool(), apool); var backwards2 = Changeset.moveOpsToNewPool(backwards, pad.apool(), apool);
var t1, t2; var t1, t2;
if (compositeStart == 0) if (compositeStart == 0)
{ {
t1 = revisionDate[0]; t1 = revisionDate[0];
} }
else else
{ {
t1 = revisionDate[compositeStart - 1]; t1 = revisionDate[compositeStart - 1];
} }
t2 = revisionDate[compositeEnd - 1]; t2 = revisionDate[compositeEnd - 1];
timeDeltas.push(t2 - t1); timeDeltas.push(t2 - t1);
forwardsChangesets.push(forwards2); forwardsChangesets.push(forwards2);
backwardsChangesets.push(backwards2); backwardsChangesets.push(backwards2);
compositeStart += granularity; compositeStart += granularity;
} }
callback(); callback();
} }
], function(err) ], function(err)
{ {
if(ERR(err, callback)) return; if(ERR(err, callback)) return;
callback(null, {forwardsChangesets: forwardsChangesets, callback(null, {forwardsChangesets: forwardsChangesets,
backwardsChangesets: backwardsChangesets, backwardsChangesets: backwardsChangesets,
apool: apool.toJsonable(), apool: apool.toJsonable(),
@ -1424,7 +1425,7 @@ function getChangesetInfo(padId, startNum, endNum, granularity, callback)
* Tries to rebuild the getPadLines function of the original Etherpad * Tries to rebuild the getPadLines function of the original Etherpad
* https://github.com/ether/pad/blob/master/etherpad/src/etherpad/control/pad/pad_changeset_control.js#L263 * https://github.com/ether/pad/blob/master/etherpad/src/etherpad/control/pad/pad_changeset_control.js#L263
*/ */
function getPadLines(padId, revNum, callback) function getPadLines(padId, revNum, callback)
{ {
var atext; var atext;
var result = {}; var result = {};
@ -1435,7 +1436,7 @@ function getPadLines(padId, revNum, callback)
function(callback) function(callback)
{ {
padManager.getPad(padId, function(err, _pad) padManager.getPad(padId, function(err, _pad)
{ {
if(ERR(err, callback)) return; if(ERR(err, callback)) return;
pad = _pad; pad = _pad;
callback(); callback();
@ -1479,7 +1480,7 @@ function getPadLines(padId, revNum, callback)
function composePadChangesets(padId, startNum, endNum, callback) function composePadChangesets(padId, startNum, endNum, callback)
{ {
var pad; var pad;
var changesets = []; var changesets = {};
var changeset; var changeset;
async.series([ async.series([
@ -1497,14 +1498,19 @@ function composePadChangesets(padId, startNum, endNum, callback)
function(callback) function(callback)
{ {
var changesetsNeeded=[]; var changesetsNeeded=[];
//create a array for all changesets, we will var headNum = pad.getHeadRevisionNumber();
if (endNum > headNum)
endNum = headNum;
if (startNum < 0)
startNum = 0;
//create a array for all changesets, we will
//replace the values with the changeset later //replace the values with the changeset later
for(var r=startNum;r<endNum;r++) for(var r=startNum;r<endNum;r++)
{ {
changesetsNeeded.push(r); changesetsNeeded.push(r);
} }
//get all changesets //get all changesets
async.forEach(changesetsNeeded, function(revNum,callback) async.forEach(changesetsNeeded, function(revNum,callback)
{ {
@ -1521,13 +1527,13 @@ function composePadChangesets(padId, startNum, endNum, callback)
{ {
changeset = changesets[startNum]; changeset = changesets[startNum];
var pool = pad.apool(); var pool = pad.apool();
for(var r=startNum+1;r<endNum;r++) for(var r=startNum+1;r<endNum;r++)
{ {
var cs = changesets[r]; var cs = changesets[r];
changeset = Changeset.compose(changeset, cs, pool); changeset = Changeset.compose(changeset, cs, pool);
} }
callback(null); callback(null);
} }
], ],

View file

@ -25,11 +25,6 @@ function setPadHTML(pad, html, callback)
{ {
var apiLogger = log4js.getLogger("ImportHtml"); var apiLogger = log4js.getLogger("ImportHtml");
// Clean the pad. This makes the rest of the code easier
// by several orders of magnitude.
pad.setText("");
var padText = pad.text();
// Parse the incoming HTML with jsdom // Parse the incoming HTML with jsdom
try{ try{
var doc = jsdom(html.replace(/>\n+</g, '><')); var doc = jsdom(html.replace(/>\n+</g, '><'));
@ -44,8 +39,15 @@ function setPadHTML(pad, html, callback)
// Convert a dom tree into a list of lines and attribute liens // Convert a dom tree into a list of lines and attribute liens
// using the content collector object // using the content collector object
var cc = contentcollector.makeContentCollector(true, null, pad.pool); var cc = contentcollector.makeContentCollector(true, null, pad.pool);
cc.collectContent(doc.childNodes[0]); try{ // we use a try here because if the HTML is bad it will blow up
cc.collectContent(doc.childNodes[0]);
}catch(e){
apiLogger.warn("HTML was not properly formed", e);
return; // We don't process the HTML because it was bad..
}
var result = cc.finish(); var result = cc.finish();
apiLogger.debug('Lines:'); apiLogger.debug('Lines:');
var i; var i;
for (i = 0; i < result.lines.length; i += 1) for (i = 0; i < result.lines.length; i += 1)
@ -90,6 +92,7 @@ function setPadHTML(pad, html, callback)
// the changeset is ready! // the changeset is ready!
var theChangeset = builder.toString(); var theChangeset = builder.toString();
apiLogger.debug('The changeset: ' + theChangeset); apiLogger.debug('The changeset: ' + theChangeset);
pad.setText("");
pad.appendRevision(theChangeset); pad.appendRevision(theChangeset);
} }