Merge branch 'soon' of github.com:xwiki-labs/cryptpad into soon
@ -1,16 +0,0 @@
|
||||
<!-- This is an HTML fragment which is included into the bottom toolbar -->
|
||||
<div>
|
||||
<div class="bottom-bar">
|
||||
<div class="bottom-bar-left">
|
||||
<span class="bottom-bar-language">
|
||||
<select id="language-selector"></select>
|
||||
</span>
|
||||
<p data-localization="bottom_france">
|
||||
</p>
|
||||
</div>
|
||||
<div class="bottom-bar-right">
|
||||
<p data-localization="bottom_support">
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
Before Width: | Height: | Size: 1.3 MiB |
Before Width: | Height: | Size: 256 KiB |
Before Width: | Height: | Size: 545 B |
@ -0,0 +1,53 @@
|
||||
define([
|
||||
'jquery',
|
||||
'/customize/application_config.js',
|
||||
'/common/cryptpad-common.js',
|
||||
'/api/config',
|
||||
], function ($, Config, Cryptpad, ApiConfig) {
|
||||
|
||||
window.APP = {
|
||||
Cryptpad: Cryptpad,
|
||||
};
|
||||
|
||||
var Messages = Cryptpad.Messages;
|
||||
|
||||
$(function () {
|
||||
// Language selector
|
||||
var $sel = $('#language-selector');
|
||||
Cryptpad.createLanguageSelector(undefined, $sel);
|
||||
$sel.find('button').addClass('btn').addClass('btn-secondary');
|
||||
$sel.show();
|
||||
|
||||
var $upgrade = $('#upgrade');
|
||||
|
||||
var showUpgrade = function (text, feedback, url) {
|
||||
if (ApiConfig.removeDonateButton) { return; }
|
||||
if (localStorage.plan) { return; }
|
||||
if (!text) { return; }
|
||||
$upgrade.text(text).show();
|
||||
$upgrade.click(function () {
|
||||
Cryptpad.feedback(feedback);
|
||||
window.open(url,'_blank');
|
||||
});
|
||||
};
|
||||
|
||||
// User admin menu
|
||||
var $userMenu = $('#user-menu');
|
||||
var userMenuCfg = {
|
||||
$initBlock: $userMenu
|
||||
};
|
||||
var $userAdmin = Cryptpad.createUserAdminMenu(userMenuCfg);
|
||||
$userAdmin.find('button').addClass('btn').addClass('btn-secondary');
|
||||
|
||||
$(window).click(function () {
|
||||
$('.cryptpad-dropdown').hide();
|
||||
});
|
||||
|
||||
if (Cryptpad.isLoggedIn() && ApiConfig.allowSubscriptions) {
|
||||
showUpgrade(Messages.upgradeAccount, "HOME_UPGRADE_ACCOUNT", Cryptpad.upgradeURL);
|
||||
} else {
|
||||
showUpgrade(Messages.supportCryptpad, "HOME_SUPPORT_CRYPTPAD", Cryptpad.donateURL);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
Before Width: | Height: | Size: 749 B |
Before Width: | Height: | Size: 780 B |
Before Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 5.8 KiB |
@ -0,0 +1,371 @@
|
||||
define(function () {
|
||||
var out = {};
|
||||
|
||||
out.main_title = "CryptPad: Zero Knowledge, Colaborare în timp real";
|
||||
out.main_slogan = "Puterea stă în cooperare - Colaborarea este cheia";
|
||||
|
||||
out.type = {};
|
||||
out.pad = "Rich text";
|
||||
out.code = "Code";
|
||||
out.poll = "Poll";
|
||||
out.slide = "Presentation";
|
||||
out.drive = "Drive";
|
||||
out.whiteboard = "Whiteboard";
|
||||
out.file = "File";
|
||||
out.media = "Media";
|
||||
|
||||
out.button_newpad = "Filă Text Nouă";
|
||||
out.button_newcode = "Filă Cod Nouă";
|
||||
out.button_newpoll = "Sondaj Nou";
|
||||
out.button_newslide = "Prezentare Nouă";
|
||||
out.button_newwhiteboard = "Fila Desen Nouă";
|
||||
out.updated_0_common_connectionLost = "<b>Conexiunea la server este pierdută</b><br>Până la revenirea conexiunii, vei fi în modul citire";
|
||||
out.common_connectionLost = out.updated_0_common_connectionLost;
|
||||
out.websocketError = "Conexiune inexistentă către serverul websocket...";
|
||||
out.typeError = "Această filă nu este compatibilă cu aplicația aleasă";
|
||||
out.onLogout = "Nu mai ești autentificat, <a href=\"/\" target=\"_blank\">apasă aici</a> să te autentifici<br>sau apasă <em>Escape</em>să accesezi fila în modul citire.";
|
||||
out.wrongApp = "Momentan nu putem arăta conținutul sesiunii în timp real în fereastra ta. Te rugăm reîncarcă pagina.";
|
||||
out.loading = "Încarcă...";
|
||||
out.error = "Eroare";
|
||||
|
||||
out.saved = "Salvat";
|
||||
out.synced = "Totul a fost salvat";
|
||||
out.deleted = "Pad șters din CryptDrive-ul tău";
|
||||
out.disconnected = "Deconectat";
|
||||
out.synchronizing = "Se sincronizează";
|
||||
out.reconnecting = "Reconectare...";
|
||||
out.lag = "Decalaj";
|
||||
out.readonly = "Mod citire";
|
||||
out.anonymous = "Anonim";
|
||||
out.yourself = "Tu";
|
||||
out.anonymousUsers = "editori anonimi";
|
||||
out.anonymousUser = "editor anonim";
|
||||
out.users = "Utilizatori";
|
||||
out.and = "Și";
|
||||
out.viewer = "privitor";
|
||||
out.viewers = "privitori";
|
||||
out.editor = "editor";
|
||||
out.editors = "editori";
|
||||
out.language = "Limbă";
|
||||
out.upgrade = "Actualizare";
|
||||
out.upgradeTitle = "Actualizează-ți contul pentru a mări limita de stocare";
|
||||
out.MB = "MB";
|
||||
out.greenLight = "Totul funcționează corespunzător";
|
||||
out.orangeLight = "Conexiunea lentă la internet îți poate afecta experiența";
|
||||
out.redLight = "Ai fost deconectat de la sesiune";
|
||||
out.pinLimitReached = "Ai atins limita de stocare";
|
||||
out.pinLimitReachedAlert = "Ai atins limita de stocare. Noile pad-uri nu vor mai fi stocate în CryptDrive.<br>Pentru a rezolva această problemă, poți să nlături pad-uri din CryptDrive-ul tău (incluzând gunoiul) sau să subscrii la un pachet premium pentru a-ți extinde spațiul de stocare.";
|
||||
out.pinLimitNotPinned = "Ai atins limita de stocare.<br>Acest pad nu va fi stocat n CryptDrive-ul tău.";
|
||||
out.pinLimitDrive = "Ai atins limita de stocare.<br>Nu poți să creezi alte pad-uri.";
|
||||
out.importButtonTitle = "Importă un pad dintr-un fișier local";
|
||||
out.exportButtonTitle = "Exportă pad-ul acesta către un fișier local";
|
||||
out.exportPrompt = "Cum ai vrea să îți denumești fișierul?";
|
||||
out.changeNamePrompt = "Schimbă-ți numele (lasă necompletat dacă vrei să fii anonim): ";
|
||||
out.user_rename = "Schimbă numele afișat";
|
||||
out.user_displayName = "Nume afișat";
|
||||
out.user_accountName = "Nume cont";
|
||||
out.clickToEdit = "Click pentru editare";
|
||||
out.forgetButtonTitle = "Mută acest pad la gunoi";
|
||||
out.forgetPrompt = "Click-ul pe OK va muta acest pad la gunoi. Ești sigur?";
|
||||
out.movedToTrash = "Acest pad a fost mutat la gunoi.<br><a href=\"/drive/\">Acesează-mi Drive-ul</a>";
|
||||
out.shareButton = "Distribuie";
|
||||
out.shareSuccess = "Link copiat în clipboard";
|
||||
out.newButton = "Nou";
|
||||
out.newButtonTitle = "Crează un nou pad";
|
||||
out.saveTemplateButton = "Salvează ca șablon";
|
||||
out.saveTemplatePrompt = "Alege un titlu pentru șablon";
|
||||
out.templateSaved = "Șablon salvat!";
|
||||
out.selectTemplate = "Selectează un șablon sau apasă escape";
|
||||
out.presentButtonTitle = "Intră în modul de prezentare";
|
||||
out.presentSuccess = "Apasă ESC pentru a ieși din modul de prezentare";
|
||||
out.backgroundButtonTitle = "Schimbă culoarea de fundal din prezentare";
|
||||
out.colorButtonTitle = "Schimbă culoarea textului în modul de prezentare";
|
||||
out.printButton = "Printează (enter)";
|
||||
out.printButtonTitle = "Printează-ți slide-urile sau exportă-le ca fișier PDF";
|
||||
out.printOptions = "Opțiuni schemă";
|
||||
out.printSlideNumber = "Afișează numărul slide-ului";
|
||||
out.printDate = "Afișează data";
|
||||
out.printTitle = "Afișează titlul pad-ului";
|
||||
out.printCSS = "Reguli de stil personalizate (CSS):";
|
||||
out.printTransition = "Permite tranziția animațiilor";
|
||||
out.slideOptionsTitle = "Personalizează-ți slide-urile";
|
||||
out.slideOptionsButton = "Salvează (enter)";
|
||||
out.editShare = "Editează link-ul";
|
||||
out.editShareTitle = "Copiază link-ul de editare în clipboard";
|
||||
out.editOpen = "Deschide link-ul de editare într-o nouă filă";
|
||||
out.editOpenTitle = "Deschide acest pad în modul de editare într-o nouă filă";
|
||||
out.viewShare = "Link în modul citire";
|
||||
out.viewShareTitle = "Copiază link-ul în modul de citire în clipboard";
|
||||
out.viewOpen = "Deschide link-ul în modul de citire într-o filă nouă";
|
||||
out.viewOpenTitle = "Deschide acest pad în modul de citire într-o nouă filă";
|
||||
out.notifyJoined = "{0} s-au alăturat sesiunii colaborative";
|
||||
out.notifyRenamed = "{0} e cunoscut ca {1}";
|
||||
out.notifyLeft = "{0} au părăsit sesiunea colaborativă";
|
||||
out.okButton = "OK (enter)";
|
||||
out.cancel = "Anulează";
|
||||
out.cancelButton = "Anulează (esc)";
|
||||
out.historyButton = "Afișează istoricul documentului";
|
||||
out.history_next = "Mergi la versiunea următoare";
|
||||
out.history_prev = "Mergi la versiunea trecută";
|
||||
out.history_goTo = "Mergi la sesiunea selectată";
|
||||
out.history_close = "Înapoi";
|
||||
out.history_closeTitle = "Închide istoricul";
|
||||
out.history_restore = "Restabilește";
|
||||
out.history_restoreTitle = "Restabilește versiunea selectată a documentului";
|
||||
out.history_restorePrompt = "Ești sigur că vrei să înlocuiești versiunea curentă a documentului cu cea afișată?";
|
||||
out.history_restoreDone = "Document restabilit";
|
||||
out.history_version = "Versiune:";
|
||||
out.poll_title = "Zero Knowledge Selector Dată";
|
||||
out.poll_subtitle = "Zero Knowledge, <em>realtime</em> programare";
|
||||
out.poll_p_save = "Setările tale sunt actualizate instant, așa că tu nu trebuie să salvezi.";
|
||||
out.poll_p_encryption = "Tot conținutul tău este criptat ca doar persoanele cărora tu le dai link-ul să aibă acces. Nici serverul nu poate să vadă ce modifici.";
|
||||
out.wizardLog = "Click pe butonul din dreapta sus pentru a te ntoarce la sondajul tău";
|
||||
out.wizardTitle = "Folosește wizard-ul pentru a crea sondajul tău";
|
||||
out.wizardConfirm = "Ești pregătit să adaugi aceste opțiuni la sondajul tău?";
|
||||
out.poll_publish_button = "Publică";
|
||||
out.poll_admin_button = "Admin";
|
||||
out.poll_create_user = "Adaugă un nou utilizator";
|
||||
out.poll_create_option = "Adaugă o nouă opțiune";
|
||||
out.poll_commit = "Comite";
|
||||
out.poll_closeWizardButton = "Închide wizard-ul";
|
||||
out.poll_closeWizardButtonTitle = "Închide wizard-ul";
|
||||
out.poll_wizardComputeButton = "Calculează Opțiunile";
|
||||
out.poll_wizardClearButton = "Curăță Tabelul";
|
||||
out.poll_wizardDescription = "Crează automat un număr de opțiuni întroducând orice număr de zile sau intervale orare";
|
||||
|
||||
out.poll_wizardAddDateButton = "+ Zi";
|
||||
out.poll_wizardAddTimeButton = "+ Ore";
|
||||
out.poll_optionPlaceholder = "Opțiune";
|
||||
out.poll_userPlaceholder = "Numele tău";
|
||||
out.poll_removeOption = "Ești sigur că vrei să îndepărtezi această opțiune?";
|
||||
out.poll_removeUser = "Ești sigur că vrei să îndepărtezi aceast utilizator?";
|
||||
out.poll_titleHint = "Titlu";
|
||||
out.poll_descriptionHint = "Descrie sondajul, și apoi folosește butonul 'publică' când ai terminat. Orice utilizator care are link-ul poate modifica descrierea, dar descurajăm această practică.";
|
||||
out.canvas_clear = "Curăță";
|
||||
out.canvas_delete = "Curăță selecția";
|
||||
out.canvas_disable = "Dezactivează modul desen";
|
||||
out.canvas_enable = "Activează modul desen";
|
||||
out.canvas_width = "Lățime";
|
||||
out.canvas_opacity = "Opacitate";
|
||||
out.fm_rootName = "Documente";
|
||||
out.fm_trashName = "Gunoi";
|
||||
out.fm_unsortedName = "Fișiere nesortate";
|
||||
out.fm_filesDataName = "Toate fișierele";
|
||||
out.fm_templateName = "Șabloane";
|
||||
out.fm_searchName = "Caută";
|
||||
out.fm_searchPlaceholder = "Caută...";
|
||||
out.fm_newButton = "Nou";
|
||||
out.fm_newButtonTitle = "Crează un nou pad sau folder";
|
||||
out.fm_newFolder = "Folder nou";
|
||||
out.fm_newFile = "Pad nou";
|
||||
out.fm_folder = "Folder";
|
||||
out.fm_folderName = "Numele folderului";
|
||||
out.fm_numberOfFolders = "# de foldere";
|
||||
out.fm_numberOfFiles = "# of files";
|
||||
out.fm_fileName = "Nume filă";
|
||||
out.fm_title = "Titlu";
|
||||
out.fm_type = "Tip";
|
||||
out.fm_lastAccess = "Ultima accesare";
|
||||
out.fm_creation = "Creare";
|
||||
out.fm_forbidden = "Acțiune interzisă";
|
||||
out.fm_originalPath = "Ruta inițială";
|
||||
out.fm_openParent = "Arată în folder";
|
||||
out.fm_noname = "Document nedenumit";
|
||||
out.fm_emptyTrashDialog = "Ești sigur că vrei să golești coșul de gunoi?";
|
||||
out.fm_removeSeveralPermanentlyDialog = "Ești sigur că vrei să ștergi pentru totdeauna aceste {0} elemente din coșul de gunoi?";
|
||||
out.fm_removePermanentlyDialog = "Ești sigur că vrei să ștergi acest element pentru totdeauna?";
|
||||
out.fm_removeSeveralDialog = "Ești sigur că vrei să muți aceste {0} elemente la coșul de gunoi?";
|
||||
out.fm_removeDialog = "Ești sigur că vrei să muți {0} la gunoi?";
|
||||
out.fm_restoreDialog = "Ești sigur că vrei să restabilești {0} în locația trecută?";
|
||||
out.fm_unknownFolderError = "Ultima locație vizitată sau cea selectată nu mai există. Deschidem fișierul părinte...";
|
||||
out.fm_contextMenuError = "Nu putem deschide meniul de context pentru acest element. Dacă problema persistă, reîncarcă pagina.";
|
||||
out.fm_selectError = "Nu putem selecta elementul vizat. Dacă problema persistă, reîncarcă pagina.";
|
||||
out.fm_categoryError = "Nu putem deschide categoria selectată, afișează sursa.";
|
||||
out.fm_info_root = "Crează câte foldere tip cuib ai nevoie pentru a-ți sorta fișierele.";
|
||||
out.fm_info_unsorted = "Conține toate fișierele pe care le-ai vizitat și nu sunt sortate în \"Documente\" sau mutate în \"Gunoi\".";
|
||||
out.fm_info_template = "Conține toate pad-urile stocate ca șabloane și pe care le poți refolosi atunci când creezi un nou pad.";
|
||||
out.fm_info_trash = "Fișierele șterse din gunoi vor fi șterse și din \"Toate fișierele\", făcând imposibilă recuperarea fișierelor din managerul de fișiere.";
|
||||
out.fm_info_allFiles = "Conține toate fișierele din \"Documente\", \"Nesortate\" și \"Gunoi\". Poți să muți sau să ștergi fișierele aici.";
|
||||
out.fm_info_login = "Loghează-te";
|
||||
out.fm_info_register = "Înscrie-te";
|
||||
out.fm_info_anonymous = "Nu ești logat cu un cont valid așa că aceste pad-uri vor fi șterse (<a href=\"https://blog.cryptpad.fr/2017/05/17/You-gotta-log-in/\" target=\"_blank\">află de ce</a>). <a href=\"/register/\">Înscrie-te</a> sau <a href=\"/login/\">Loghează-te</a> pentru a le salva.";
|
||||
out.fm_alert_backupUrl = "Link copie de rezervă pentru acest drive.<br> Este <strong>foarte recomandat</strong> să o păstrezi pentru tine.<br>Poți să o folosești pentru a recupera toate fișierele în cazul în care memoria browserului tău este șterge..<br>Oricine are linkul poate să editeze sau să îndepărteze toate fișierele din managerul tău de documente.<br>";
|
||||
out.fm_alert_anonymous = "Salut, momentan folosești CryptPad în mod anonim. Este ok, doar că fișierele tale vor fi șterse după o perioadă de inactivitate. Am dezactivat caracteristicile avansate ale drive-ului pentru utilizatorii anonimi pentru a face clar faptul că stocare documentelor acolo nu este o metodă sigură. Poți să <a href=\"https://blog.cryptpad.fr/2017/05/17/You-gotta-log-in/\" target=\"_blank\">citești mai multe</a> despre motivarea noastră și despre ce de trebuie să te <a href=\"/register/\">Înregistrezi</a> si sa te <a href=\"/login/\">Loghezi</a>.";
|
||||
out.fm_backup_title = "Link de backup";
|
||||
out.fm_nameFile = "Cum ai vrea să numești fișierul?";
|
||||
out.fc_newfolder = "Folder nou";
|
||||
out.fc_rename = "Redenumește";
|
||||
out.fc_open = "Deschide";
|
||||
out.fc_open_ro = "Deschide (modul citire)";
|
||||
out.fc_delete = "Șterge";
|
||||
out.fc_restore = "Restaurează";
|
||||
out.fc_remove = "Șterge permanent";
|
||||
out.fc_empty = "Curăță coșul";
|
||||
out.fc_prop = "Proprietăți";
|
||||
out.fc_sizeInKilobytes = "Dimensiune n Kilobytes";
|
||||
out.fo_moveUnsortedError = "Nu poți să muți un folder la lista de pad-uri nesortate";
|
||||
out.fo_existingNameError = "Numele ales este deja folosit în acest director. Te rugăm să alegi altul.";
|
||||
out.fo_moveFolderToChildError = "Nu poți să muți un folder într-unul dintre descendenții săi";
|
||||
out.fo_unableToRestore = "Nu am reușit să restaurăm fișierul în locația de origine. Poți să ncerci să îl muți într-o nouă locație.";
|
||||
out.fo_unavailableName = "Un fișier sau un folder cu același nume există deja în locația nouă. Redenumește elementul și încearcă din nou.";
|
||||
out.login_login = "Loghează-te";
|
||||
out.login_makeAPad = "Crează un pad în modul anonim";
|
||||
out.login_nologin = "Răsfoiește pad-urile locale";
|
||||
out.login_register = "Înscrie-te";
|
||||
out.logoutButton = "Deloghează-te";
|
||||
out.settingsButton = "Setări";
|
||||
out.login_username = "Nume utilizator";
|
||||
out.login_password = "Parolă";
|
||||
out.login_confirm = "Confirmă parola";
|
||||
out.login_remember = "Ține-mă minte";
|
||||
out.login_hashing = "Încriptăm parola, o să mai dureze.";
|
||||
out.login_hello = "Salut {0},";
|
||||
out.login_helloNoName = "Salut,";
|
||||
out.login_accessDrive = "Acesează-ți drive-ul";
|
||||
out.login_orNoLogin = "sau";
|
||||
out.login_noSuchUser = "Nume de utilizator sau parolă invalide. Încearcă din nou sau înscrie-te.";
|
||||
out.login_invalUser = "Nume utilizator cerut";
|
||||
out.login_invalPass = "Parolă cerută";
|
||||
out.login_unhandledError = "O eroare neașteptată a avut loc emoticon_unhappy";
|
||||
out.register_importRecent = "Importă istoricul pad-ului (Recomandat)";
|
||||
out.register_acceptTerms = "Accept <a href='/terms.html'>termenii serviciului</a>";
|
||||
out.register_passwordsDontMatch = "Parolele nu se potrivesc!";
|
||||
out.register_mustAcceptTerms = "Trebuie să accepți termenii serviciului";
|
||||
out.register_mustRememberPass = "Nu putem să îți resetăm parola dacă o uiți. Este foarte important să o ții minte! Bifează căsuța pentru a confirma.";
|
||||
out.register_header = "Bine ai venit în CryptPad";
|
||||
out.register_explanation = "<p>Hai să stabilim câteva lucruri, mai întâi</p><ul><li>Parola ta este cheia secretă care criptează toate pad-urile tale. Dacă pierzi/uiți parola nu există nici-o metodă prin care îți putem recupera datele.</li><li>Poți importa pad-uri care au fost vizionate recent în browser pentru a le avea în cont.</li><li>Dacă folosești un computer împărțit, trebuie să te deloghezi, închiderea taburilor nu este de ajuns.</li></ul>";
|
||||
out.register_writtenPassword = "Mi-am notat numele de utilizator și parola, înaintează.";
|
||||
out.register_cancel = "Întoarce-te";
|
||||
out.register_warning = "Zero Knowledge înseamnă că noi nu îți putem recupera datele dacă îți pierzi parola.";
|
||||
out.register_alreadyRegistered = "Acest user există deja, vrei să te loghezi?";
|
||||
out.settings_title = "Setări";
|
||||
out.settings_save = "Salvează";
|
||||
out.settings_backupTitle = "Fă o copie de rezervă sau restaurează toate datele";
|
||||
out.settings_backup = "Copie de rezervă";
|
||||
out.settings_restore = "Restaurează";
|
||||
out.settings_resetTitle = "Curăță-ți drive-ul";
|
||||
out.settings_reset = "Îndepărtează toate fișierele și folderele din CryptPad-ul tău.";
|
||||
out.settings_resetPrompt = "Această acțiune va indepărta toate pad-urile din drive-ul tău.<br>Ești sigur că vrei să continui?<br>Tastează “<em>Iubesc CryptPad</em>” pentru a confirma.";
|
||||
out.settings_resetDone = "Drive-ul tău este acum gol!";
|
||||
out.settings_resetError = "Text de verificare incorect. CryptPad-ul tău nu a fost schimbat.";
|
||||
out.settings_resetTips = "Sfaturi în CryptDrive";
|
||||
out.settings_resetTipsButton = "Resetează sfaturile disponibile în CryptDrive";
|
||||
out.settings_resetTipsDone = "Toate sfaturile sunt vizibile din nou.";
|
||||
out.settings_importTitle = "Importă pad-urile recente ale acestui browser n CryptDrive-ul meu";
|
||||
out.settings_import = "Importă";
|
||||
out.settings_importConfirm = "Ești sigur că vrei să imporți pad-urile recente ale acestui browser în contul tău de CryptDrive?";
|
||||
out.settings_importDone = "Import complet";
|
||||
out.settings_userFeedbackHint1 = "CryptPad oferă niște feedback foarte simplu serverului, pentru a ne informa cum putem să îți îmbunătățim experiența voastră.";
|
||||
out.settings_userFeedbackHint2 = "Conținutul pad-ului tău nu va fi împărțit cu serverele.";
|
||||
out.settings_userFeedback = "Activează feedback";
|
||||
out.settings_anonymous = "Nu ești logat. Setările sunt specifice browser-ului.";
|
||||
out.settings_publicSigningKey = "Cheia de semnătură publică";
|
||||
out.settings_usage = "Uzaj";
|
||||
out.settings_usageTitle = "Vezi dimensiunea totală a pad-urilor fixate în MB";
|
||||
out.settings_pinningNotAvailable = "Pad-urile fixate sunt disponibile doar utilizatorilor înregistrați.";
|
||||
out.settings_pinningError = "Ceva nu a funcționat";
|
||||
out.settings_usageAmount = "Pad-urile tale fixate ocupă {0}MB";
|
||||
out.settings_logoutEverywhereTitle = "Deloghează-te peste tot";
|
||||
out.settings_logoutEverywhere = "Deloghează-te din toate sesiunile web";
|
||||
out.settings_logoutEverywhereConfirm = "Ești sigur? Va trebui să te loghezi, din nou, pe toate device-urile tale.";
|
||||
out.upload_serverError = "Eroare de server: fișierele tale nu pot fi încărcate la momentul acesta.";
|
||||
out.upload_uploadPending = "Ai deja o încărcare în desfășurare. Anulezi și încarci noul fișier?";
|
||||
out.upload_success = "Fișierul tău ({0}) a fost ncărcat și adăugat la drive-ul tău cu succes.";
|
||||
out.main_p2 = "Acest proiect folosește <a href=\"http://ckeditor.com/\">CKEditor</a> Visual Editor, <a href=\"https://codemirror.net/\">CodeMirror</a>, și <a href=\"https://github.com/xwiki-contrib/chainpad\">ChainPad</a> un motor în timp real.";
|
||||
out.main_howitworks_p1 = "CryptPad folosește o variantă a algoritmului de <a href=\"https://en.wikipedia.org/wiki/Operational_transformation\">Operational transformation</a> care este capabil să găsescă consens distribuit folosind <a href=\"https://bitcoin.org/bitcoin.pdf\">Nakamoto Blockchain</a>, o construcție popularizată de <a href=\"https://en.wikipedia.org/wiki/Bitcoin\">Bitcoin</a>. Astfel algoritmul poate evita nevoia ca serverul central să rezove conflicte, iar serverul nu este interesat de conținutul care este editat în pad.";
|
||||
out.main_about_p2 = "Dacă ai orice fel de întrebare sau comentariu, poți să ne <a href=\"https://twitter.com/cryptpad\">dai un tweet</a>, semnalezi o problemă <a href=\"https://github.com/xwiki-labs/cryptpad/issues/\" title=\"index de probleme\">on github</a>, spui salut pe IRC (<a href=\"http://webchat.freenode.net?channels=%23cryptpad&uio=MT1mYWxzZSY5PXRydWUmMTE9Mjg3JjE1PXRydWUe7\" title=\"freenode webchat\">irc.freenode.net</a>), sau <a href=\"research@xwiki.com\">trimiți un email</a>.";
|
||||
out.main_info = "<h1>Colaborează în siguranță</h1><br> Dezvoltă-ți ideile împreună cu documentele partajate în timp ce tehnologia <strong>Zero Knowledge</strong> îți păstrează securitatea; chiar și de noi.";
|
||||
out.main_howitworks = "Cum funcționează";
|
||||
out.main_zeroKnowledge = "Zero Knowledge";
|
||||
out.main_zeroKnowledge_p = "Nu trebuie să ne crezi că <em>nu ne uităm</em> la pad-urile tale, cu tehnologia revoluționară Zero Knowledge a CryptPad <em>nu putem</em>. Învață mai multe despre cum îți protejăm <a href=\"/privacy.html\" title='Intimitatea'>Intimitate și Securitate</a>.";
|
||||
out.main_writeItDown = "Notează";
|
||||
out.main_writeItDown_p = "Cele mai importante proiecte vin din idei mici. Notează-ți momentele de inspirație și ideile neașteptate pentru că nu știi niciodată care ar putea fi noua mare descoperire.";
|
||||
out.main_share = "Partajează link-ul, partajează pad-ul";
|
||||
out.main_share_p = "Dezvoltă-ți ideile împreună: organizează întâlniri eficiente, colaborează pe liste TODO și fă prezentări scurte cu toți prietenii tăi și device-urile tale.";
|
||||
out.main_organize = "Organizează-te";
|
||||
out.main_organize_p = "Cu CryptPad Drive, poți să stai cu ochii pe ce este important. Folderele îți permit să ții evidența proiectelor tale și să ai o viziune globală asupra evoluției lucrurilor.";
|
||||
out.tryIt = "Testează!";
|
||||
out.main_richText = "Rich Text editor";
|
||||
out.main_richText_p = "Editează texte complexe în mod colaborativ cu Zero Knowledge în timp real. <a href=\"http://ckeditor.com\" target=\"_blank\">CkEditor</a> application.";
|
||||
out.main_code = "Editor cod";
|
||||
out.main_code_p = "Editează cod din softul tău, în mod colaborativ, cu Zero Knowledge în timp real.<a href=\"https://www.codemirror.net\" target=\"_blank\">CodeMirror</a> application.";
|
||||
out.main_slide = "Editor slide-uri";
|
||||
out.main_slide_p = "Crează-ți prezentări folosind sintaxa Markdown, și afișează-le în browser-ul tău.";
|
||||
out.main_poll = "Sondaj";
|
||||
out.main_poll_p = "Plănuiește întâlniri sau evenimente, sau votează pentru cea mai bună soluție pentru problema ta.";
|
||||
out.main_drive = "CryptDrive";
|
||||
out.footer_applications = "Aplicații";
|
||||
out.footer_contact = "Contact";
|
||||
out.footer_aboutUs = "Despre noi";
|
||||
out.about = "Despre";
|
||||
out.privacy = "Privacy";
|
||||
out.contact = "Contact";
|
||||
out.terms = "ToS";
|
||||
out.blog = "Blog";
|
||||
out.policy_title = "Politica de confidențialitate CryptPad";
|
||||
out.policy_whatweknow = "Ce știm despre tine";
|
||||
out.policy_whatweknow_p1 = "Ca o aplicație care este găzduită online, CryptPad are acces la metadatele expuse de protocolul HTTP. Asta include adresa IP-ului tău, și alte titluri HTTP care pot fi folosite ca să identifice un browser. Poți să vezi ce informații împărtășește browser-ul tău vizitând <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://www.whatismybrowser.com/detect/what-http-headers-is-my-browser-sending\" title=\"what http headers is my browser sending\">WhatIsMyBrowser.com</a>.";
|
||||
out.policy_whatweknow_p2 = "Folosim <a href=\"https://www.elastic.co/products/kibana\" target=\"_blank\" rel=\"noopener noreferrer\" title=\"platforma de analiză open source\">Kibana</a>, o platformă open source, pentru a afla mai multe despre utilizatorii noștri. Kibana ne spune despre cum ai găsit CryptPad, căutare directă, printr-un motor de căutare, sau prin recomandare de la un alt serviciu online ca Reddit sau Twitter.";
|
||||
out.policy_howweuse = "Cum folosim ce aflăm";
|
||||
out.policy_howweuse_p1 = "Folosim aceste informații pentru a lua decizii mai bune în promovarea CryptPad, prin evaluarea eforturilor trecute care au fost de succes. Informațiile despre locația ta ne ajută să aflăm dacă ar trebui să oferim suport pentru alte limbi, pe lângă engleză.";
|
||||
out.policy_howweuse_p2 = "Informațiile despre browser-ul tău (dacă este bazat pe un sistem de operare desktop sau mobil) ne ajută să luăm decizii când prioritizăm viitoarele îmbunătățiri. Echipa noastră de dezvoltare este mică, și încercăm să facem alegeri care să îmbunătățească experiența câtor mai mulți utilizatori.";
|
||||
|
||||
out.policy_whatwetell = "Ce le spunem altora despre tine";
|
||||
out.policy_whatwetell_p1 = "Nu furnizăm informațiile obținute terților, decât dacă ne este cerut în mod legal.";
|
||||
out.policy_links = "Link-uri către alte site-uri";
|
||||
out.policy_links_p1 = "Acest site conține link-uri către alte site-uri, incluzându-le pe cele produse de alte organizații. Nu suntem responsabili pentru practicile de intimitate sau pentru conținutul site-urilor externe. Ca regulă generală, link-urile către site-uri externe sunt deschise ntr-o fereastră noup, pentru a face clar faptul că părăsiți CryptPad.fr.";
|
||||
out.policy_ads = "Reclame";
|
||||
out.policy_ads_p1 = "Nu afișăm nici o formă de publicitate online, dar s-ar putea să atașăm link-uri către instituțiile care ne finanțează cerecetarea.";
|
||||
out.policy_choices = "Ce alegeri ai";
|
||||
out.policy_choices_open = "Codul nostru este open source, așa că tu ai mereu posibilitatea de a-ți găzdui propria instanță de CryptPad.";
|
||||
out.policy_choices_vpn = "Dacă vrei să folosești instanța găzduită de noi, dar nu vrei să îți expui IP-ul, poți să îl protejezi folosind <a href=\"https://www.torproject.org/projects/torbrowser.html.en\" title=\"downloads from the Tor project\" target=\"_blank\" rel=\"noopener noreferrer\">Tor browser bundle</a>, sau <a href=\"https://riseup.net/en/vpn\" title=\"VPNs provided by Riseup\" target=\"_blank\" rel=\"noopener noreferrer\">VPN</a>.";
|
||||
out.policy_choices_ads = "Dacă vrei doar să blochezi platforma noastră de analiză, poți folosi soluții de adblocking ca <a href=\"https://www.eff.org/privacybadger\" title=\"download privacy badger\" target=\"_blank\" rel=\"noopener noreferrer\">Privacy Badger</a>.";
|
||||
out.tos_title = "CryptPad Termeni de Utilizare";
|
||||
out.tos_legal = "Te rugăm să nu fii rău intenționat, abuziv, sau să faci orice ilegal.";
|
||||
out.tos_availability = "Sperăm că o să găsești acest serviciu util, dar disponibilitatea sau performanța nu poate fi garantată. Te rugăm să îți exporți datele n mod regulat.";
|
||||
out.tos_e2ee = "Conținutul CryptPad poate fi citit sau modificat de oricine care poate ghici sau obține fragmentul identificator al pad-ului. Recomandăm să folosești soluții de comunicare criptate end-to-end-encrypted (e2ee) pentru a partaja link-uri, evitând orice risc în cazul unei scurgeri de informații.";
|
||||
out.tos_logs = "Metadatele oferite de browser-ul tău serverului ar putea fi înscrise în scopul de a menține serviciul.";
|
||||
out.tos_3rdparties = "Nu oferim date personale terților, decât dacă ne sunt solicitate prin lege.";
|
||||
out.bottom_france = "<a href=\"http://www.xwiki.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Realizat cu <img class=\"bottom-bar-heart\" src=\"/customize/heart.png\" alt=\"love\" /> n <img class=\"bottom-bar-fr\" src=\"/customize/fr.png\" alt=\"Franța\" /></a>";
|
||||
out.bottom_support = "<a href=\"http://labs.xwiki.com/\" title=\"XWiki Labs\" target=\"_blank\" rel=\"noopener noreferrer\">Un proiect al <img src=\"/customize/logo-xwiki2.png\" alt=\"XWiki SAS\" class=\"bottom-bar-xwiki\"/> Labs Project </a> cu susținerea <a href=\"http://ng.open-paas.org/\" title=\"OpenPaaS::ng\" target=\"_blank\" rel=\"noopener noreferrer\"> <img src=\"/customize/openpaasng.png\" alt=\"OpenPaaS-ng\" class=\"bottom-bar-openpaas\" /></a>";
|
||||
out.header_france = "<a href=\"http://www.xwiki.com/\" target=\"_blank\" rel=\"noopener noreferrer\">With <img class=\"bottom-bar-heart\" src=\"/customize/heart.png\" alt=\"love\" /> from <img class=\"bottom-bar-fr\" src=\"/customize/fr.png\" title=\"Franța\" alt=\"Franța\"/> by <img src=\"/customize/logo-xwiki.png\" alt=\"XWiki SAS\" class=\"bottom-bar-xwiki\"/></a>";
|
||||
out.header_support = "<a href=\"http://ng.open-paas.org/\" title=\"OpenPaaS::ng\" target=\"_blank\" rel=\"noopener noreferrer\"> <img src=\"/customize/openpaasng.png\" alt=\"OpenPaaS-ng\" class=\"bottom-bar-openpaas\" /></a>";
|
||||
out.header_logoTitle = "Mergi la pagina principală";
|
||||
out.initialState = "<span style=\"font-size:16px;\"><p>Acesta este <strong>CryptPad</strong>, editorul colaborativ bazat pe tehnologia Zero Knowledge în timp real. Totul este salvat pe măsură ce scrii.<br>Partajează link-ul către acest pad pentru a edita cu prieteni sau folosește <span style=\"background-color:#5cb85c;color:#ffffff;\"> Share </span> butonul pentru a partaja <em>read-only link</em> permițând vizualizarea dar nu și editarea.</p><p><span style=\"color:#808080;\"><em>Îndrăznește, începe să scrii...</em></span></p></span><p> <br></p>";
|
||||
out.codeInitialState = "/*\n Acesta este editorul colaborativ de cod bazat pe tehnologia Zero Knowledge CryptPad.\n Ce scrii aici este criptat, așa că doar oamenii care au link-ul pot să-l acceseze.\n Poți să alegi ce limbaj de programare pus n evidență și schema de culori UI n dreapta sus.\n*/";
|
||||
out.slideInitialState = "# CryptSlide\n* Acesta este un editor colaborativ bazat pe tehnologia Zero Knowledge.\n* Ce scrii aici este criptat, așa că doar oamenii care au link-ul pot să-l acceseze.\n* Nici măcar serverele nu au acces la ce scrii tu.\n* Ce vezi aici, ce auzi aici, atunci când pleci, lași aici.\n\n-\n# Cum se folosește\n1. Scrie-ți conținutul slide-urilor folosind sintaxa markdown\n - Află mai multe despre sintaxa markdown [aici](http://www.markdowntutorial.com/)\n2. Separă-ți slide-urile cu -\n3. Click pe butonul \"Play\" pentru a vedea rezultatele - Slide-urile tale sunt actualizate în timp real.";
|
||||
out.driveReadmeTitle = "Ce este CryptDrive?";
|
||||
out.readme_welcome = "Bine ai venit n CryptPad !";
|
||||
out.readme_p1 = "Bine ai venit în CryptPad, acesta este locul unde îți poți lua notițe, singur sau cu prietenii.";
|
||||
out.readme_p2 = "Acest pad o să îți ofere un scurt ghid în cum poți să folosești CryptPad pentru a lua notițe, a le ține organizate și a colabora pe ele.";
|
||||
out.readme_cat1 = "Descoperă-ți CryptDrive-ul";
|
||||
out.readme_cat1_l1 = "Crează un pad: În CryptDrive-ul tău, dă click {0} apoi {1} și poți să creezi un pad.";
|
||||
out.readme_cat1_l2 = "Deschide pad-urile din CryptDrive-ul tău: doublu-click pe iconița unui pad pentru a-l deschide.";
|
||||
out.readme_cat1_l3 = "Organizează-ți pad-urile: Când ești logat, orice pad accesezi va fi afișat ca în secțiunea {0} a drive-ului tău.";
|
||||
out.readme_cat1_l3_l1 = "Poți să folosești funcția click and drag pentru a muta fișierele în folderele secțiunii {0} a drive-ului tău și pentru a crea noi foldere.";
|
||||
out.readme_cat1_l3_l2 = "Ține minte să încerci click-dreapta pe iconițe pentru că există și meniuri adiționale.";
|
||||
out.readme_cat1_l4 = "Pune pad-urile vechi în gunoi. Poți să folosești funcția click and drag pe pad-uri în categoria {0} la fel ca și în cazul folderelor.";
|
||||
out.readme_cat2 = "Crează pad-uri ca un profesionist";
|
||||
out.edit = "editează";
|
||||
out.view = "vezi";
|
||||
out.readme_cat2_l1 = "Butonul {0} din pad-ul tău dă accesul colaboratorilor tăi să {1} sau să {2} pad-ul.";
|
||||
out.readme_cat2_l2 = "Schimbă titlul pad-ului dând click pe creion";
|
||||
out.readme_cat3 = "Descoperă aplicațiile CryptPad";
|
||||
out.readme_cat3_l1 = "Cu editorul de cod CryptPad, poți colabora pe cod ca Javascript și markdown ca HTML și Markdown";
|
||||
out.readme_cat3_l2 = "Cu editorul de slide-uri CryptPad, poți să faci prezentări scurte folosind Markdown";
|
||||
out.readme_cat3_l3 = "Cu CryptPoll poți să organizezi votări rapide, mai ales pentru a programa ntâlniri care se potrivesc calendarelor tuturor";
|
||||
|
||||
out.tips = { };
|
||||
out.tips.lag = "Iconița verde din dreapta-sus arată calitatea conexiunii internetului tău la serverele CryptPad.";
|
||||
out.tips.shortcuts = "`ctrl+b`, `ctrl+i` and `ctrl+u` sunt scurtături pentru bold, italic și underline.";
|
||||
out.tips.indentare = "În listele cu bulină sau cele numerotate, poți folosi tab sau shift+tab pentru a mări sau micșora indentarea.";
|
||||
out.tips.titlu = "Poți seta titlul pad-urilor tale prin click pe centru sus.";
|
||||
out.tips.stocare = "De fiecare dată când vizitezi un pad, dacă ești logat va fi salvat pe CryptDrive-ul tău.";
|
||||
out.tips.marker = "Poți sublinia text într-un pad folosind itemul \"marker\" n meniul de stiluri.";
|
||||
|
||||
out.feedback_about = "Dacă citești asta, probabil că ești curios de ce CryptPad cere pagini web atunci când întreprinzi anumite acțiuni";
|
||||
out.feedback_privacy = "Ne pasă de intimitatea ta, si în același timp vrem să păstrăm CryptPad ușor de folosit. Folosim acest fișier pentru a ne da seama care beneficii UI contează cel mai mult pentru utilizatori, cerându-l alături de un parametru specific atunci când acțiunea se desfășoară";
|
||||
out.feedback_optout = "Dacă vrei să ieși, vizitează <a href='/settings/'>setările de pe pagina ta de user</a>, unde vei găsi o căsuță pentru a activa sau dezactiva feedback-ul de la user";
|
||||
|
||||
return out;
|
||||
});
|
@ -1,3 +1,9 @@
|
||||
# This file is included strictly as an example of how Nginx can be configured
|
||||
# to work with CryptPad. This example WILL NOT WORK AS IS. For best results,
|
||||
# compare the sections of this configuration file against a working CryptPad
|
||||
# installation (http server by the Nodejs process). If you are using CryptPad
|
||||
# in production, contact sales@cryptpad.fr
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
|
@ -0,0 +1,115 @@
|
||||
/* jshint esversion: 6, node: true */
|
||||
const Fs = require('fs');
|
||||
const Semaphore = require('saferphore');
|
||||
const nThen = require('nthen');
|
||||
|
||||
const hashesFromPinFile = (pinFile, fileName) => {
|
||||
var pins = {};
|
||||
pinFile.split('\n').filter((x)=>(x)).map((l) => JSON.parse(l)).forEach((l) => {
|
||||
switch (l[0]) {
|
||||
case 'RESET': {
|
||||
pins = {};
|
||||
//jshint -W086
|
||||
// fallthrough
|
||||
}
|
||||
case 'PIN': {
|
||||
l[1].forEach((x) => { pins[x] = 1; });
|
||||
break;
|
||||
}
|
||||
case 'UNPIN': {
|
||||
l[1].forEach((x) => { delete pins[x]; });
|
||||
break;
|
||||
}
|
||||
default: throw new Error(JSON.stringify(l) + ' ' + fileName);
|
||||
}
|
||||
});
|
||||
return Object.keys(pins);
|
||||
};
|
||||
|
||||
const sizeForHashes = (hashes, dsFileStats) => {
|
||||
let sum = 0;
|
||||
hashes.forEach((h) => {
|
||||
const s = dsFileStats[h];
|
||||
if (typeof(s) !== 'number') {
|
||||
//console.log('missing ' + h + ' ' + typeof(s));
|
||||
} else {
|
||||
sum += s.size;
|
||||
}
|
||||
});
|
||||
return sum;
|
||||
};
|
||||
|
||||
const sema = Semaphore.create(20);
|
||||
|
||||
let dirList;
|
||||
const fileList = [];
|
||||
const dsFileStats = {};
|
||||
const out = [];
|
||||
const pinned = {};
|
||||
|
||||
nThen((waitFor) => {
|
||||
Fs.readdir('./datastore', waitFor((err, list) => {
|
||||
if (err) { throw err; }
|
||||
dirList = list;
|
||||
}));
|
||||
}).nThen((waitFor) => {
|
||||
dirList.forEach((f) => {
|
||||
sema.take((returnAfter) => {
|
||||
Fs.readdir('./datastore/' + f, waitFor(returnAfter((err, list2) => {
|
||||
if (err) { throw err; }
|
||||
list2.forEach((ff) => { fileList.push('./datastore/' + f + '/' + ff); });
|
||||
})));
|
||||
});
|
||||
});
|
||||
}).nThen((waitFor) => {
|
||||
fileList.forEach((f) => {
|
||||
sema.take((returnAfter) => {
|
||||
Fs.stat(f, waitFor(returnAfter((err, st) => {
|
||||
if (err) { throw err; }
|
||||
dsFileStats[f.replace(/^.*\/([^\/]*)\.ndjson$/, (all, a) => (a))] = st;
|
||||
})));
|
||||
});
|
||||
});
|
||||
}).nThen((waitFor) => {
|
||||
Fs.readdir('./pins', waitFor((err, list) => {
|
||||
if (err) { throw err; }
|
||||
dirList = list;
|
||||
}));
|
||||
}).nThen((waitFor) => {
|
||||
fileList.splice(0, fileList.length);
|
||||
dirList.forEach((f) => {
|
||||
sema.take((returnAfter) => {
|
||||
Fs.readdir('./pins/' + f, waitFor(returnAfter((err, list2) => {
|
||||
if (err) { throw err; }
|
||||
list2.forEach((ff) => { fileList.push('./pins/' + f + '/' + ff); });
|
||||
})));
|
||||
});
|
||||
});
|
||||
}).nThen((waitFor) => {
|
||||
fileList.forEach((f) => {
|
||||
sema.take((returnAfter) => {
|
||||
Fs.readFile(f, waitFor(returnAfter((err, content) => {
|
||||
if (err) { throw err; }
|
||||
const hashes = hashesFromPinFile(content.toString('utf8'), f);
|
||||
const size = sizeForHashes(hashes, dsFileStats);
|
||||
if (process.argv.indexOf('--unpinned') > -1) {
|
||||
hashes.forEach((x) => { pinned[x] = 1; });
|
||||
} else {
|
||||
out.push([f, Math.floor(size / (1024 * 1024))]);
|
||||
}
|
||||
})));
|
||||
});
|
||||
});
|
||||
}).nThen(() => {
|
||||
if (process.argv.indexOf('--unpinned') > -1) {
|
||||
Object.keys(dsFileStats).forEach((f) => {
|
||||
if (!(f in pinned)) {
|
||||
console.log("./datastore/" + f.slice(0,2) + "/" + f + ".ndjson " +
|
||||
dsFileStats[f].size + " " + (+dsFileStats[f].mtime));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
out.sort((a,b) => (a[1] - b[1]));
|
||||
out.forEach((x) => { console.log(x[0] + ' ' + x[1] + ' MB'); });
|
||||
}
|
||||
});
|
@ -0,0 +1,74 @@
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
}
|
||||
body {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
max-height: 100%;
|
||||
min-height: auto;
|
||||
}
|
||||
.CodeMirror {
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
width: 50%;
|
||||
transition: width 500ms, min-width 500ms, max-width 500ms;
|
||||
min-width: 20%;
|
||||
max-width: 80%;
|
||||
resize: horizontal;
|
||||
}
|
||||
.CodeMirror.fullPage {
|
||||
min-width: 100%;
|
||||
max-width: 100%;
|
||||
resize: none;
|
||||
}
|
||||
.CodeMirror-focused .cm-matchhighlight {
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFklEQVQI12NgYGBgkKzc8x9CMDAwAAAmhwSbidEoSQAAAABJRU5ErkJggg==);
|
||||
background-position: bottom;
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
#editorContainer {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
#previewContainer {
|
||||
flex: 1;
|
||||
padding: 5px 20px;
|
||||
overflow: auto;
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
border-left: 1px solid black;
|
||||
box-sizing: border-box;
|
||||
font-family: Calibri, Ubuntu, sans-serif;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
#preview {
|
||||
max-width: 40vw;
|
||||
margin: auto;
|
||||
}
|
||||
#preview table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
#preview table tr th {
|
||||
border: 3px solid black;
|
||||
padding: 15px;
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
.CodeMirror {
|
||||
flex: 1;
|
||||
max-width: 100%;
|
||||
resize: none;
|
||||
}
|
||||
#previewContainer {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
@import "../../customize.dist/src/less/variables.less";
|
||||
@import "../../customize.dist/src/less/mixins.less";
|
||||
|
||||
html, body{
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
}
|
||||
body {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
max-height: 100%;
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
@slideTime: 500ms;
|
||||
.CodeMirror {
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
width: 50%;
|
||||
transition: width @slideTime, min-width @slideTime, max-width @slideTime;
|
||||
min-width: 20%;
|
||||
max-width: 80%;
|
||||
resize: horizontal;
|
||||
}
|
||||
.CodeMirror.fullPage {
|
||||
min-width: 100%;
|
||||
max-width: 100%;
|
||||
resize: none;
|
||||
}
|
||||
.CodeMirror-focused .cm-matchhighlight {
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFklEQVQI12NgYGBgkKzc8x9CMDAwAAAmhwSbidEoSQAAAABJRU5ErkJggg==);
|
||||
background-position: bottom;
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
#editorContainer {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
#previewContainer {
|
||||
flex: 1;
|
||||
padding: 5px 20px;
|
||||
overflow: auto;
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
border-left: 1px solid black;
|
||||
box-sizing: border-box;
|
||||
font-family: Calibri,Ubuntu,sans-serif;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
#preview {
|
||||
max-width: 40vw;
|
||||
margin: auto;
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
tr {
|
||||
th {
|
||||
border: 3px solid black;
|
||||
padding: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: @media-medium-screen) {
|
||||
.CodeMirror {
|
||||
flex: 1;
|
||||
max-width: 100%;
|
||||
resize: none;
|
||||
}
|
||||
#previewContainer {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,300 @@
|
||||
define([
|
||||
'jquery',
|
||||
'/common/modes.js',
|
||||
'/common/themes.js',
|
||||
'/bower_components/file-saver/FileSaver.min.js'
|
||||
], function ($, Modes, Themes) {
|
||||
var saveAs = window.saveAs;
|
||||
var module = {};
|
||||
|
||||
module.create = function (CMeditor, ifrw, Cryptpad) {
|
||||
var exp = {};
|
||||
|
||||
var Messages = Cryptpad.Messages;
|
||||
|
||||
var CodeMirror = exp.CodeMirror = CMeditor;
|
||||
CodeMirror.modeURL = "/bower_components/codemirror/mode/%N/%N.js";
|
||||
|
||||
var $pad = $('#pad-iframe');
|
||||
var $textarea = exp.$textarea = $pad.contents().find('#editor1');
|
||||
|
||||
var Title;
|
||||
var onLocal = function () {};
|
||||
var $rightside;
|
||||
exp.init = function (local, title, toolbar) {
|
||||
if (typeof local === "function") {
|
||||
onLocal = local;
|
||||
}
|
||||
Title = title;
|
||||
$rightside = toolbar.$rightside;
|
||||
};
|
||||
|
||||
var editor = exp.editor = CMeditor.fromTextArea($textarea[0], {
|
||||
lineNumbers: true,
|
||||
lineWrapping: true,
|
||||
autoCloseBrackets: true,
|
||||
matchBrackets : true,
|
||||
showTrailingSpace : true,
|
||||
styleActiveLine : true,
|
||||
search: true,
|
||||
highlightSelectionMatches: {showToken: /\w+/},
|
||||
extraKeys: {"Shift-Ctrl-R": undefined},
|
||||
foldGutter: true,
|
||||
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
|
||||
mode: "javascript",
|
||||
readOnly: true
|
||||
});
|
||||
editor.setValue(Messages.codeInitialState);
|
||||
|
||||
var setMode = exp.setMode = function (mode, cb) {
|
||||
exp.highlightMode = mode;
|
||||
if (mode === 'text') {
|
||||
editor.setOption('mode', 'text');
|
||||
if (cb) { cb('text'); }
|
||||
return;
|
||||
}
|
||||
CMeditor.autoLoadMode(editor, mode);
|
||||
editor.setOption('mode', mode);
|
||||
if (exp.$language) {
|
||||
var name = exp.$language.find('a[data-value="' + mode + '"]').text() || 'Mode';
|
||||
exp.$language.setValue(name);
|
||||
}
|
||||
if(cb) { cb(mode); }
|
||||
};
|
||||
|
||||
var setTheme = exp.setTheme = (function () {
|
||||
var path = '/common/theme/';
|
||||
|
||||
var $head = $(ifrw.document.head);
|
||||
|
||||
var themeLoaded = exp.themeLoaded = function (theme) {
|
||||
return $head.find('link[href*="'+theme+'"]').length;
|
||||
};
|
||||
|
||||
var loadTheme = exp.loadTheme = function (theme) {
|
||||
$head.append($('<link />', {
|
||||
rel: 'stylesheet',
|
||||
href: path + theme + '.css',
|
||||
}));
|
||||
};
|
||||
|
||||
return function (theme, $select) {
|
||||
if (!theme) {
|
||||
editor.setOption('theme', 'default');
|
||||
} else {
|
||||
if (!themeLoaded(theme)) {
|
||||
loadTheme(theme);
|
||||
}
|
||||
editor.setOption('theme', theme);
|
||||
}
|
||||
if ($select) {
|
||||
$select.setValue(theme || 'Theme');
|
||||
}
|
||||
};
|
||||
}());
|
||||
|
||||
exp.getHeadingText = function () {
|
||||
var lines = editor.getValue().split(/\n/);
|
||||
|
||||
var text = '';
|
||||
lines.some(function (line) {
|
||||
// lisps?
|
||||
var lispy = /^\s*(;|#\|)(.*?)$/;
|
||||
if (lispy.test(line)) {
|
||||
line.replace(lispy, function (a, one, two) {
|
||||
text = two;
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
// lines beginning with a hash are potentially valuable
|
||||
// works for markdown, python, bash, etc.
|
||||
var hash = /^#(.*?)$/;
|
||||
if (hash.test(line)) {
|
||||
line.replace(hash, function (a, one) {
|
||||
text = one;
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
// lines including a c-style comment are also valuable
|
||||
var clike = /^\s*(\/\*|\/\/)(.*)?(\*\/)*$/;
|
||||
if (clike.test(line)) {
|
||||
line.replace(clike, function (a, one, two) {
|
||||
if (!(two && two.replace)) { return; }
|
||||
text = two.replace(/\*\/\s*$/, '').trim();
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO make one more pass for multiline comments
|
||||
});
|
||||
|
||||
return text.trim();
|
||||
};
|
||||
|
||||
exp.configureLanguage = function (cb, onModeChanged) {
|
||||
var options = [];
|
||||
Modes.list.forEach(function (l) {
|
||||
options.push({
|
||||
tag: 'a',
|
||||
attributes: {
|
||||
'data-value': l.mode,
|
||||
'href': '#',
|
||||
},
|
||||
content: l.language // Pretty name of the language value
|
||||
});
|
||||
});
|
||||
var dropdownConfig = {
|
||||
text: 'Mode', // Button initial text
|
||||
options: options, // Entries displayed in the menu
|
||||
left: true, // Open to the left of the button
|
||||
isSelect: true,
|
||||
};
|
||||
var $block = exp.$language = Cryptpad.createDropdown(dropdownConfig);
|
||||
$block.find('a').click(function () {
|
||||
setMode($(this).attr('data-value'), onModeChanged);
|
||||
onLocal();
|
||||
});
|
||||
|
||||
if ($rightside) { $rightside.append($block); }
|
||||
if (cb) { cb(); }
|
||||
};
|
||||
|
||||
exp.configureTheme = function (cb) {
|
||||
/* Remember the user's last choice of theme using localStorage */
|
||||
var themeKey = 'CRYPTPAD_CODE_THEME';
|
||||
var lastTheme = localStorage.getItem(themeKey) || 'default';
|
||||
|
||||
var options = [];
|
||||
Themes.forEach(function (l) {
|
||||
options.push({
|
||||
tag: 'a',
|
||||
attributes: {
|
||||
'data-value': l.name,
|
||||
'href': '#',
|
||||
},
|
||||
content: l.name // Pretty name of the language value
|
||||
});
|
||||
});
|
||||
var dropdownConfig = {
|
||||
text: 'Theme', // Button initial text
|
||||
options: options, // Entries displayed in the menu
|
||||
left: true, // Open to the left of the button
|
||||
isSelect: true,
|
||||
initialValue: lastTheme
|
||||
};
|
||||
var $block = exp.$theme = Cryptpad.createDropdown(dropdownConfig);
|
||||
|
||||
setTheme(lastTheme, $block);
|
||||
|
||||
$block.find('a').click(function () {
|
||||
var theme = $(this).attr('data-value');
|
||||
setTheme(theme, $block);
|
||||
localStorage.setItem(themeKey, theme);
|
||||
});
|
||||
|
||||
if ($rightside) { $rightside.append($block); }
|
||||
if (cb) { cb(); }
|
||||
};
|
||||
|
||||
exp.exportText = function () {
|
||||
var text = editor.getValue();
|
||||
|
||||
var ext = Modes.extensionOf(exp.highlightMode);
|
||||
|
||||
var title = Cryptpad.fixFileName(Title ? Title.suggestTitle('cryptpad') : "?") + (ext || '.txt');
|
||||
|
||||
Cryptpad.prompt(Messages.exportPrompt, title, function (filename) {
|
||||
if (filename === null) { return; }
|
||||
var blob = new Blob([text], {
|
||||
type: 'text/plain;charset=utf-8'
|
||||
});
|
||||
saveAs(blob, filename);
|
||||
});
|
||||
};
|
||||
exp.importText = function (content, file) {
|
||||
var $bar = ifrw.$('#cme_toolbox');
|
||||
var mode;
|
||||
var mime = CodeMirror.findModeByMIME(file.type);
|
||||
|
||||
if (!mime) {
|
||||
var ext = /.+\.([^.]+)$/.exec(file.name);
|
||||
if (ext[1]) {
|
||||
mode = CMeditor.findModeByExtension(ext[1]);
|
||||
}
|
||||
} else {
|
||||
mode = mime && mime.mode || null;
|
||||
}
|
||||
|
||||
if (mode && Modes.list.some(function (o) { return o.mode === mode; })) {
|
||||
setMode(mode);
|
||||
$bar.find('#language-mode').val(mode);
|
||||
} else {
|
||||
console.log("Couldn't find a suitable highlighting mode: %s", mode);
|
||||
setMode('text');
|
||||
$bar.find('#language-mode').val('text');
|
||||
}
|
||||
|
||||
editor.setValue(content);
|
||||
onLocal();
|
||||
};
|
||||
|
||||
var cursorToPos = function(cursor, oldText) {
|
||||
var cLine = cursor.line;
|
||||
var cCh = cursor.ch;
|
||||
var pos = 0;
|
||||
var textLines = oldText.split("\n");
|
||||
for (var line = 0; line <= cLine; line++) {
|
||||
if(line < cLine) {
|
||||
pos += textLines[line].length+1;
|
||||
}
|
||||
else if(line === cLine) {
|
||||
pos += cCh;
|
||||
}
|
||||
}
|
||||
return pos;
|
||||
};
|
||||
|
||||
var posToCursor = function(position, newText) {
|
||||
var cursor = {
|
||||
line: 0,
|
||||
ch: 0
|
||||
};
|
||||
var textLines = newText.substr(0, position).split("\n");
|
||||
cursor.line = textLines.length - 1;
|
||||
cursor.ch = textLines[cursor.line].length;
|
||||
return cursor;
|
||||
};
|
||||
|
||||
exp.setValueAndCursor = function (oldDoc, remoteDoc, TextPatcher) {
|
||||
var scroll = editor.getScrollInfo();
|
||||
//get old cursor here
|
||||
var oldCursor = {};
|
||||
oldCursor.selectionStart = cursorToPos(editor.getCursor('from'), oldDoc);
|
||||
oldCursor.selectionEnd = cursorToPos(editor.getCursor('to'), oldDoc);
|
||||
|
||||
editor.setValue(remoteDoc);
|
||||
editor.save();
|
||||
|
||||
var op = TextPatcher.diff(oldDoc, remoteDoc);
|
||||
var selects = ['selectionStart', 'selectionEnd'].map(function (attr) {
|
||||
return TextPatcher.transformCursor(oldCursor[attr], op);
|
||||
});
|
||||
|
||||
if(selects[0] === selects[1]) {
|
||||
editor.setCursor(posToCursor(selects[0], remoteDoc));
|
||||
}
|
||||
else {
|
||||
editor.setSelection(posToCursor(selects[0], remoteDoc), posToCursor(selects[1], remoteDoc));
|
||||
}
|
||||
|
||||
editor.scrollTo(scroll.left, scroll.top);
|
||||
};
|
||||
|
||||
return exp;
|
||||
};
|
||||
|
||||
return module;
|
||||
});
|
||||
|
@ -0,0 +1,51 @@
|
||||
define(function () {
|
||||
var module = {};
|
||||
|
||||
module.create = function (UserList, Title, cfg) {
|
||||
var exp = {};
|
||||
|
||||
exp.update = function (shjson) {
|
||||
// Extract the user list (metadata) from the hyperjson
|
||||
var json = (!shjson || typeof shjson !== "string") ? "" : JSON.parse(shjson);
|
||||
var titleUpdated = false;
|
||||
var metadata;
|
||||
if (Array.isArray(json)) {
|
||||
metadata = json[3] && json[3].metadata;
|
||||
} else {
|
||||
metadata = json.metadata;
|
||||
}
|
||||
if (typeof metadata === "object") {
|
||||
if (metadata.users) {
|
||||
var userData = metadata.users;
|
||||
// Update the local user data
|
||||
UserList.addToUserData(userData);
|
||||
}
|
||||
if (metadata.defaultTitle) {
|
||||
Title.updateDefaultTitle(metadata.defaultTitle);
|
||||
}
|
||||
if (typeof metadata.title !== "undefined") {
|
||||
Title.updateTitle(metadata.title || Title.defaultTitle);
|
||||
titleUpdated = true;
|
||||
}
|
||||
if (metadata.slideOptions && cfg.slideOptions) {
|
||||
cfg.slideOptions(metadata.slideOptions);
|
||||
}
|
||||
if (metadata.color && cfg.slideColors) {
|
||||
cfg.slideColors(metadata.color, metadata.backColor);
|
||||
}
|
||||
if (typeof(metadata.palette) !== 'undefined' && cfg.updatePalette) {
|
||||
cfg.updatePalette(metadata.palette);
|
||||
}
|
||||
}
|
||||
if (!titleUpdated) {
|
||||
Title.updateTitle(Title.defaultTitle);
|
||||
}
|
||||
};
|
||||
|
||||
return exp;
|
||||
};
|
||||
|
||||
return module;
|
||||
});
|
||||
|
||||
|
@ -0,0 +1,84 @@
|
||||
define(function () {
|
||||
var module = {};
|
||||
|
||||
module.create = function (cfg, onLocal, Cryptpad) {
|
||||
var exp = {};
|
||||
|
||||
var parsed = exp.parsedHref = Cryptpad.parsePadUrl(window.location.href);
|
||||
exp.defaultTitle = Cryptpad.getDefaultName(parsed);
|
||||
|
||||
exp.title = document.title; // TOOD slides
|
||||
|
||||
cfg = cfg || {};
|
||||
|
||||
var getHeadingText = cfg.getHeadingText || function () { return; };
|
||||
var updateLocalTitle = function (newTitle) {
|
||||
exp.title = newTitle;
|
||||
if (typeof cfg.updateLocalTitle === "function") {
|
||||
cfg.updateLocalTitle(newTitle);
|
||||
} else {
|
||||
document.title = newTitle;
|
||||
}
|
||||
};
|
||||
|
||||
var $title;
|
||||
exp.setToolbar = function (toolbar) {
|
||||
$title = toolbar && toolbar.title;
|
||||
};
|
||||
|
||||
exp.getTitle = function () { return exp.title; };
|
||||
var isDefaultTitle = exp.isDefaultTitle = function (){return exp.title === exp.defaultTitle;};
|
||||
|
||||
var suggestTitle = exp.suggestTitle = function (fallback) {
|
||||
if (isDefaultTitle()) {
|
||||
return getHeadingText() || fallback || "";
|
||||
} else {
|
||||
return exp.title || getHeadingText() || exp.defaultTitle;
|
||||
}
|
||||
};
|
||||
|
||||
var renameCb = function (err, newTitle) {
|
||||
if (err) { return; }
|
||||
updateLocalTitle(newTitle);
|
||||
console.log('here');
|
||||
onLocal();
|
||||
};
|
||||
|
||||
exp.updateTitle = function (newTitle) {
|
||||
if (newTitle === exp.title) { return; }
|
||||
// Change the title now, and set it back to the old value if there is an error
|
||||
var oldTitle = exp.title;
|
||||
Cryptpad.renamePad(newTitle, function (err, data) {
|
||||
if (err) {
|
||||
console.log("Couldn't set pad title");
|
||||
console.error(err);
|
||||
updateLocalTitle(oldTitle);
|
||||
return;
|
||||
}
|
||||
updateLocalTitle(data);
|
||||
if (!$title) { return; }
|
||||
$title.find('span.title').text(data);
|
||||
$title.find('input').val(data);
|
||||
});
|
||||
};
|
||||
|
||||
exp.updateDefaultTitle = function (newDefaultTitle) {
|
||||
exp.defaultTitle = newDefaultTitle;
|
||||
if (!$title) { return; }
|
||||
$title.find('input').attr("placeholder", exp.defaultTitle);
|
||||
};
|
||||
|
||||
exp.getTitleConfig = function () {
|
||||
return {
|
||||
onRename: renameCb,
|
||||
suggestName: suggestTitle,
|
||||
defaultName: exp.defaultTitle
|
||||
};
|
||||
};
|
||||
|
||||
return exp;
|
||||
};
|
||||
|
||||
return module;
|
||||
});
|
||||
|
@ -0,0 +1,127 @@
|
||||
define([
|
||||
'jquery',
|
||||
'/bower_components/marked/marked.min.js',
|
||||
'/bower_components/diff-dom/diffDOM.js'
|
||||
],function ($, Marked) {
|
||||
var DiffMd = {};
|
||||
|
||||
var DiffDOM = window.diffDOM;
|
||||
var renderer = new Marked.Renderer();
|
||||
|
||||
Marked.setOptions({
|
||||
renderer: renderer
|
||||
});
|
||||
|
||||
DiffMd.render = function (md) {
|
||||
return Marked(md);
|
||||
};
|
||||
|
||||
// Tasks list
|
||||
var checkedTaskItemPtn = /^\s*\[x\]\s*/;
|
||||
var uncheckedTaskItemPtn = /^\s*\[ \]\s*/;
|
||||
renderer.listitem = function (text) {
|
||||
var isCheckedTaskItem = checkedTaskItemPtn.test(text);
|
||||
var isUncheckedTaskItem = uncheckedTaskItemPtn.test(text);
|
||||
if (isCheckedTaskItem) {
|
||||
text = text.replace(checkedTaskItemPtn,
|
||||
'<i class="fa fa-check-square" aria-hidden="true"></i> ') + '\n';
|
||||
}
|
||||
if (isUncheckedTaskItem) {
|
||||
text = text.replace(uncheckedTaskItemPtn,
|
||||
'<i class="fa fa-square-o" aria-hidden="true"></i> ') + '\n';
|
||||
}
|
||||
var cls = (isCheckedTaskItem || isUncheckedTaskItem) ? ' class="todo-list-item"' : '';
|
||||
return '<li'+ cls + '>' + text + '</li>\n';
|
||||
};
|
||||
|
||||
var forbiddenTags = [
|
||||
'SCRIPT',
|
||||
'IFRAME',
|
||||
'OBJECT',
|
||||
'APPLET',
|
||||
'VIDEO',
|
||||
'AUDIO',
|
||||
];
|
||||
var unsafeTag = function (info) {
|
||||
if (['addAttribute', 'modifyAttribute'].indexOf(info.diff.action) !== -1) {
|
||||
if (/^on/.test(info.diff.name)) {
|
||||
console.log("Rejecting forbidden element attribute with name", info.diff.name);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (['addElement', 'replaceElement'].indexOf(info.diff.action) !== -1) {
|
||||
var msg = "Rejecting forbidden tag of type (%s)";
|
||||
if (info.diff.element && forbiddenTags.indexOf(info.diff.element.nodeName) !== -1) {
|
||||
console.log(msg, info.diff.element.nodeName);
|
||||
return true;
|
||||
} else if (info.diff.newValue && forbiddenTags.indexOf(info.diff.newValue.nodeName) !== -1) {
|
||||
console.log("Replacing restricted element type (%s) with PRE", info.diff.newValue.nodeName);
|
||||
info.diff.newValue.nodeName = 'PRE';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var slice = function (coll) {
|
||||
return Array.prototype.slice.call(coll);
|
||||
};
|
||||
|
||||
/* remove listeners from the DOM */
|
||||
var removeListeners = function (root) {
|
||||
slice(root.attributes).map(function (attr) {
|
||||
if (/^on/.test(attr.name)) {
|
||||
root.attributes.removeNamedItem(attr.name);
|
||||
}
|
||||
});
|
||||
// all the way down
|
||||
slice(root.children).forEach(removeListeners);
|
||||
};
|
||||
|
||||
var domFromHTML = function (html) {
|
||||
var Dom = new DOMParser().parseFromString(html, "text/html");
|
||||
removeListeners(Dom.body);
|
||||
return Dom;
|
||||
};
|
||||
|
||||
var DD = new DiffDOM({
|
||||
preDiffApply: function (info) {
|
||||
if (unsafeTag(info)) { return true; }
|
||||
}
|
||||
});
|
||||
|
||||
var makeDiff = function (A, B, id) {
|
||||
var Err;
|
||||
var Els = [A, B].map(function (frag) {
|
||||
if (typeof(frag) === 'object') {
|
||||
if (!frag || (frag && !frag.body)) {
|
||||
Err = "No body";
|
||||
return;
|
||||
}
|
||||
var els = frag.body.querySelectorAll('#'+id);
|
||||
if (els.length) {
|
||||
return els[0];
|
||||
}
|
||||
}
|
||||
Err = 'No candidate found';
|
||||
});
|
||||
if (Err) { return Err; }
|
||||
var patch = DD.diff(Els[0], Els[1]);
|
||||
return patch;
|
||||
};
|
||||
|
||||
DiffMd.apply = function (newHtml, $content) {
|
||||
var id = $content.attr('id');
|
||||
if (!id) { throw new Error("The element must have a valid id"); }
|
||||
var $div = $('<div>', {id: id}).append(newHtml);
|
||||
var Dom = domFromHTML($('<div>').append($div).html());
|
||||
var oldDom = domFromHTML($content[0].outerHTML);
|
||||
var patch = makeDiff(oldDom, Dom, id);
|
||||
if (typeof(patch) === 'string') {
|
||||
throw new Error(patch);
|
||||
} else {
|
||||
DD.apply($content[0], patch);
|
||||
}
|
||||
};
|
||||
|
||||
return DiffMd;
|
||||
});
|
||||
|
@ -0,0 +1,75 @@
|
||||
define([], function () {
|
||||
var out = function () { };
|
||||
out.passed = out.failed = out;
|
||||
if (window.location.hash.indexOf("?test=test") > -1) {
|
||||
var cpt = window.__CRYPTPAD_TEST__ = {
|
||||
data: [],
|
||||
getData: function () {
|
||||
var data = JSON.stringify(cpt.data);
|
||||
cpt.data = [];
|
||||
return data;
|
||||
}
|
||||
};
|
||||
|
||||
// jshint -W103
|
||||
var errProto = (new Error()).__proto__;
|
||||
var doLog = function (o) {
|
||||
var s;
|
||||
if (typeof(o) === 'object' && o.__proto__ === errProto) {
|
||||
s = JSON.stringify([ o.message, o.stack ]);
|
||||
} else if (typeof(s) !== 'string') {
|
||||
try {
|
||||
s = JSON.stringify(o);
|
||||
} catch (e) {
|
||||
s = String(o);
|
||||
}
|
||||
}
|
||||
var out = [s];
|
||||
try { throw new Error(); } catch (e) { out.push(e.stack.split('\n')[3]); }
|
||||
cpt.data.push({ type: 'log', val: out.join('') });
|
||||
};
|
||||
|
||||
window.console._error = window.console.error;
|
||||
window.console._log = window.console.log;
|
||||
window.console.error = function (e) { window.console._error(e); doLog(e); };
|
||||
window.console.log = function (l) { window.console._log(l); doLog(l); };
|
||||
|
||||
window.onerror = function (msg, url, lineNo, columnNo, e) {
|
||||
cpt.data.push({
|
||||
type: 'report',
|
||||
val: 'failed',
|
||||
error: {
|
||||
message: e ? e.message : msg,
|
||||
stack: e ? e.stack : (url + ":" + lineNo)
|
||||
}
|
||||
});
|
||||
};
|
||||
require.onError = function (e) {
|
||||
cpt.data.push({
|
||||
type: 'report',
|
||||
val: 'failed',
|
||||
error: { message: e.message, stack: e.stack }
|
||||
});
|
||||
};
|
||||
out = function (f) { f(); };
|
||||
out.testing = true;
|
||||
out.passed = function () {
|
||||
cpt.data.push({
|
||||
type: 'report',
|
||||
val: 'passed'
|
||||
});
|
||||
};
|
||||
out.failed = function (reason) {
|
||||
var e;
|
||||
try { throw new Error(reason); } catch (err) { e = err; }
|
||||
cpt.data.push({
|
||||
type: 'report',
|
||||
val: 'failed',
|
||||
error: { message: e.message, stack: e.stack }
|
||||
});
|
||||
};
|
||||
} else {
|
||||
out.testing = false;
|
||||
}
|
||||
return out;
|
||||
});
|
@ -0,0 +1,118 @@
|
||||
html,
|
||||
body {
|
||||
margin: 0px;
|
||||
height: 100%;
|
||||
}
|
||||
.cryptpad-toolbar {
|
||||
margin-bottom: 1px;
|
||||
padding: 0px;
|
||||
display: inline-block;
|
||||
}
|
||||
#file,
|
||||
#dl {
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
border: 2px solid black;
|
||||
}
|
||||
.inputfile {
|
||||
width: 0.1px;
|
||||
height: 0.1px;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
}
|
||||
#upload-form,
|
||||
#download-form {
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
position: relative;
|
||||
width: 50vh;
|
||||
height: 50vh;
|
||||
display: block;
|
||||
margin: 50px auto;
|
||||
max-width: 80vw;
|
||||
}
|
||||
#upload-form label,
|
||||
#download-form label {
|
||||
line-height: calc(50vh - 20px);
|
||||
text-align: center;
|
||||
position: relative;
|
||||
padding: 10px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
height: 50vh;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.hovering {
|
||||
background-color: rgba(255, 0, 115, 0.5) !important;
|
||||
}
|
||||
.block {
|
||||
display: block;
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
.inputfile + label {
|
||||
border: 2px solid black;
|
||||
background-color: rgba(50, 50, 50, 0.1);
|
||||
display: block;
|
||||
}
|
||||
.inputfile:focus + label,
|
||||
.inputfile + label:hover {
|
||||
background-color: rgba(50, 50, 50, 0.3);
|
||||
}
|
||||
#progress {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
transition: width 500ms;
|
||||
width: 0%;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
background-color: rgba(255, 0, 115, 0.75);
|
||||
z-index: 10000;
|
||||
display: block;
|
||||
}
|
||||
#status {
|
||||
display: none;
|
||||
width: 80vw;
|
||||
margin-top: 50px;
|
||||
margin-left: 10vw;
|
||||
border: 1px solid black;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
#status tr:nth-child(1) {
|
||||
background-color: #ccc;
|
||||
border: 1px solid #999;
|
||||
}
|
||||
#status tr:nth-child(1) td {
|
||||
text-align: center;
|
||||
}
|
||||
#status td {
|
||||
border-left: 1px solid #BBB;
|
||||
border-right: 1px solid #BBB;
|
||||
padding: 0 10px;
|
||||
}
|
||||
#status .upProgress {
|
||||
width: 200px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
}
|
||||
#status .progressContainer {
|
||||
position: absolute;
|
||||
width: 0px;
|
||||
left: 5px;
|
||||
top: 1px;
|
||||
bottom: 1px;
|
||||
background-color: rgba(0, 0, 255, 0.3);
|
||||
}
|
||||
#status .upCancel {
|
||||
text-align: center;
|
||||
}
|
||||
#status .fa.cancel {
|
||||
color: #ff0073;
|
||||
}
|
@ -0,0 +1,124 @@
|
||||
@import "../../customize.dist/src/less/variables.less";
|
||||
@import "../../customize.dist/src/less/mixins.less";
|
||||
|
||||
@button-border: 2px;
|
||||
|
||||
html, body {
|
||||
margin: 0px;
|
||||
height: 100%;
|
||||
}
|
||||
.cryptpad-toolbar {
|
||||
margin-bottom: 1px;
|
||||
padding: 0px;
|
||||
display: inline-block;
|
||||
}
|
||||
#file, #dl {
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
border: @button-border solid black;
|
||||
}
|
||||
|
||||
.inputfile {
|
||||
width: 0.1px;
|
||||
height: 0.1px;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
#upload-form, #download-form {
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
|
||||
position: relative;
|
||||
width: 50vh;
|
||||
height: 50vh;
|
||||
display: block;
|
||||
margin: 50px auto;
|
||||
max-width: 80vw;
|
||||
label {
|
||||
line-height: ~"calc(50vh - 20px)";
|
||||
text-align: center;
|
||||
position: relative;
|
||||
padding: 10px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
height: 50vh;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
.hovering {
|
||||
background-color: rgba(255, 0, 115, 0.5) !important;
|
||||
}
|
||||
|
||||
.block {
|
||||
display: block;
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
.inputfile + label {
|
||||
border: 2px solid black;
|
||||
background-color: rgba(50, 50, 50, .10);
|
||||
display: block;
|
||||
}
|
||||
|
||||
.inputfile:focus + label,
|
||||
.inputfile + label:hover {
|
||||
background-color: rgba(50, 50, 50, 0.30);
|
||||
}
|
||||
|
||||
#progress {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
|
||||
|
||||
transition: width 500ms;
|
||||
width: 0%;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
background-color: rgba(255, 0, 115, 0.75);
|
||||
z-index: 10000;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#status {
|
||||
display: none;
|
||||
width: 80vw;
|
||||
margin-top: 50px;
|
||||
margin-left: 10vw;
|
||||
border: 1px solid black;
|
||||
border-collapse: collapse;
|
||||
tr:nth-child(1) {
|
||||
background-color: #ccc;
|
||||
border: 1px solid #999;
|
||||
td { text-align: center; }
|
||||
}
|
||||
td {
|
||||
border-left: 1px solid #BBB;
|
||||
border-right: 1px solid #BBB;
|
||||
padding: 0 10px;
|
||||
}
|
||||
.upProgress {
|
||||
width: 200px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
}
|
||||
.progressContainer {
|
||||
position: absolute;
|
||||
width: 0px;
|
||||
left: 5px;
|
||||
top: 1px; bottom: 1px;
|
||||
background-color: rgba(0,0,255,0.3);
|
||||
}
|
||||
.upCancel { text-align: center; }
|
||||
.fa.cancel {
|
||||
color: rgb(255, 0, 115);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="cp pad">
|
||||
<head>
|
||||
<title>CryptPad</title>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="/bower_components/components-font-awesome/css/font-awesome.min.css">
|
||||
<script data-bootload="main.js" data-main="/common/boot.js" src="/bower_components/requirejs/require.js"></script>
|
||||
<link rel="icon" type="image/png"
|
||||
href="/customize/main-favicon.png"
|
||||
data-main-favicon="/customize/main-favicon.png"
|
||||
data-alt-favicon="/customize/alt-favicon.png"
|
||||
id="favicon" />
|
||||
<link rel="stylesheet" href="/customize/main.css" />
|
||||
</head>
|
||||
<body>
|
@ -0,0 +1,87 @@
|
||||
define([
|
||||
'jquery',
|
||||
'/bower_components/chainpad-crypto/crypto.js',
|
||||
'/bower_components/chainpad-netflux/chainpad-netflux.js',
|
||||
'/common/toolbar.js',
|
||||
'/common/cryptpad-common.js',
|
||||
'/common/visible.js',
|
||||
'/common/notify.js',
|
||||
'/file/file-crypto.js',
|
||||
'/bower_components/tweetnacl/nacl-fast.min.js',
|
||||
'/bower_components/file-saver/FileSaver.min.js',
|
||||
], function ($, Crypto, realtimeInput, Toolbar, Cryptpad, Visible, Notify, FileCrypto) {
|
||||
var Nacl = window.nacl;
|
||||
$(function () {
|
||||
|
||||
var filesAreSame = function (a, b) {
|
||||
var l = a.length;
|
||||
if (l !== b.length) { return false; }
|
||||
|
||||
var i = 0;
|
||||
for (; i < l; i++) { if (a[i] !== b[i]) { return false; } }
|
||||
return true;
|
||||
};
|
||||
|
||||
var metadataIsSame = function (A, B) {
|
||||
return !Object.keys(A).some(function (k) {
|
||||
return A[k] !== B[k];
|
||||
});
|
||||
};
|
||||
|
||||
var upload = function (blob, metadata) {
|
||||
var u8 = new Uint8Array(blob);
|
||||
var key = Nacl.randomBytes(32);
|
||||
|
||||
var next = FileCrypto.encrypt(u8, metadata, key);
|
||||
|
||||
var chunks = [];
|
||||
var sendChunk = function (box, cb) {
|
||||
chunks.push(box);
|
||||
cb();
|
||||
};
|
||||
|
||||
var again = function (err, box) {
|
||||
if (err) { throw new Error(err); }
|
||||
|
||||
if (box) {
|
||||
return void sendChunk(box, function (e) {
|
||||
if (e) {
|
||||
console.error(e);
|
||||
return Cryptpad.alert('Something went wrong');
|
||||
}
|
||||
next(again);
|
||||
});
|
||||
}
|
||||
// check if the uploaded file can be decrypted
|
||||
var newU8 = FileCrypto.joinChunks(chunks);
|
||||
|
||||
console.log('encrypted file with metadata is %s uint8s', newU8.length);
|
||||
FileCrypto.decrypt(newU8, key, function (e, res) {
|
||||
if (e) { return Cryptpad.alert(e); }
|
||||
|
||||
if (filesAreSame(blob, res.content) &&
|
||||
metadataIsSame(res.metadata, metadata)) {
|
||||
Cryptpad.alert("successfully uploaded");
|
||||
} else {
|
||||
Cryptpad.alert('encryption failure!');
|
||||
}
|
||||
});
|
||||
};
|
||||
next(again);
|
||||
};
|
||||
|
||||
var andThen = function () {
|
||||
var src = '/customize/cryptofist_mini.png';
|
||||
Cryptpad.fetch(src, function (e, file) {
|
||||
console.log('original file is %s uint8s', file.length);
|
||||
upload(file, {
|
||||
pew: 'pew',
|
||||
bang: 'bang',
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
andThen();
|
||||
|
||||
});
|
||||
});
|