manual merge
commit
840cd91bcb
|
@ -1,6 +1,4 @@
|
|||
(function () {
|
||||
var LS_LANG = "CRYPTPAD_LANG";
|
||||
|
||||
// add your module to this map so it gets used
|
||||
var map = {
|
||||
'fr': 'Français',
|
||||
|
@ -12,17 +10,17 @@ var map = {
|
|||
'zh': '繁體中文',
|
||||
};
|
||||
|
||||
var messages = {};
|
||||
var LS_LANG = "CRYPTPAD_LANG";
|
||||
var getStoredLanguage = function () { return localStorage.getItem(LS_LANG); };
|
||||
var getBrowserLanguage = function () { return navigator.language || navigator.userLanguage; };
|
||||
var getLanguage = function () {
|
||||
var getBrowserLanguage = function () { return navigator.language || navigator.userLanguage || ''; };
|
||||
var getLanguage = messages._getLanguage = function () {
|
||||
if (window.cryptpadLanguage) { return window.cryptpadLanguage; }
|
||||
if (getStoredLanguage()) { return getStoredLanguage(); }
|
||||
var l = getBrowserLanguage() || '';
|
||||
if (Object.keys(map).indexOf(l) !== -1) {
|
||||
return l;
|
||||
}
|
||||
var l = getBrowserLanguage();
|
||||
// Edge returns 'fr-FR' --> transform it to 'fr' and check again
|
||||
return Object.keys(map).indexOf(l.split('-')[0]) !== -1 ? l.split('-')[0] : 'en';
|
||||
return map[l] ? l :
|
||||
(map[l.split('-')[0]] ? l.split('-')[0] : 'en');
|
||||
};
|
||||
var language = getLanguage();
|
||||
|
||||
|
@ -30,21 +28,18 @@ var req = ['jquery', '/customize/translations/messages.js'];
|
|||
if (language && map[language]) { req.push('/customize/translations/messages.' + language + '.js'); }
|
||||
|
||||
define(req, function($, Default, Language) {
|
||||
|
||||
var externalMap = JSON.parse(JSON.stringify(map));
|
||||
|
||||
map.en = 'English';
|
||||
var defaultLanguage = 'en';
|
||||
console.log(messages);
|
||||
|
||||
var messages;
|
||||
|
||||
if (!Language || !language || language === defaultLanguage || language === 'default' || !map[language]) {
|
||||
messages = Default;
|
||||
if (!Language || language === defaultLanguage || !map[language]) {
|
||||
messages = $.extend(true, messages, Default);
|
||||
}
|
||||
else {
|
||||
// Add the translated keys to the returned object
|
||||
messages = $.extend(true, {}, Default, Language);
|
||||
messages = $.extend(true, messages, Default, Language);
|
||||
}
|
||||
console.log(messages);
|
||||
|
||||
messages._languages = map;
|
||||
messages._languageUsed = language;
|
||||
|
@ -53,12 +48,12 @@ define(req, function($, Default, Language) {
|
|||
if (typeof(cb) !== "function") { return; }
|
||||
var missing = [];
|
||||
var reqs = [];
|
||||
Object.keys(externalMap).forEach(function (code) {
|
||||
Object.keys(map).forEach(function (code) {
|
||||
reqs.push('/customize/translations/messages.' + code + '.js');
|
||||
});
|
||||
require(reqs, function () {
|
||||
var langs = arguments;
|
||||
Object.keys(externalMap).forEach(function (code, i) {
|
||||
Object.keys(map).forEach(function (code, i) {
|
||||
var translation = langs[i];
|
||||
var updated = {};
|
||||
Object.keys(Default).forEach(function (k) {
|
||||
|
@ -107,59 +102,6 @@ define(req, function($, Default, Language) {
|
|||
}
|
||||
};
|
||||
|
||||
// Add handler to the language selector
|
||||
var storeLanguage = function (l) {
|
||||
localStorage.setItem(LS_LANG, l);
|
||||
};
|
||||
messages._initSelector = function ($select) {
|
||||
var selector = $select || $('#language-selector');
|
||||
|
||||
if (!selector.length) { return; }
|
||||
|
||||
// Select the current language in the list
|
||||
selector.setValue(language || 'English');
|
||||
|
||||
// Listen for language change
|
||||
$(selector).find('a.languageValue').on('click', function () {
|
||||
var newLanguage = $(this).attr('data-value');
|
||||
storeLanguage(newLanguage);
|
||||
if (newLanguage !== language) {
|
||||
window.location.reload();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var translateText = function (i, e) {
|
||||
var $el = $(e);
|
||||
var key = $el.data('localization');
|
||||
$el.html(messages[key]);
|
||||
};
|
||||
var translateAppend = function (i, e) {
|
||||
var $el = $(e);
|
||||
var key = $el.data('localization-append');
|
||||
$el.append(messages[key]);
|
||||
};
|
||||
var translateTitle = function () {
|
||||
var $el = $(this);
|
||||
var key = $el.data('localization-title');
|
||||
$el.attr('title', messages[key]);
|
||||
};
|
||||
var translatePlaceholder = function () {
|
||||
var $el = $(this);
|
||||
var key = $el.data('localization-placeholder');
|
||||
$el.attr('placeholder', messages[key]);
|
||||
};
|
||||
messages._applyTranslation = function () {
|
||||
$('[data-localization]').each(translateText);
|
||||
$('[data-localization-append]').each(translateAppend);
|
||||
$('[data-localization-title]').each(translateTitle);
|
||||
$('[data-localization-placeholder]').each(translatePlaceholder);
|
||||
$('#pad-iframe').contents().find('[data-localization]').each(translateText);
|
||||
$('#pad-iframe').contents().find('[data-localization-append]').each(translateAppend);
|
||||
$('#pad-iframe').contents().find('[data-localization-title]').each(translateTitle);
|
||||
$('#pad-iframe').contents().find('[data-localization-placeholder]').each(translatePlaceholder);
|
||||
};
|
||||
|
||||
messages.driveReadme = '["BODY",{"class":"cke_editable cke_editable_themed cke_contents_ltr cke_show_borders","contenteditable":"true","spellcheck":"false","style":"color: rgb(51, 51, 51);"},' +
|
||||
'[["H1",{},["'+messages.readme_welcome+'"]],["P",{},["'+messages.readme_p1+'"]],["P",{},["'+messages.readme_p2+'"]],["HR",{},[]],["H2",{},["'+messages.readme_cat1+'",["BR",{},[]]]],["UL",{},[["LI",{},["'+messages._getKey("readme_cat1_l1", ['",["STRONG",{},["'+messages.newButton+'"]],"', '",["STRONG",{},["'+messages.type.pad+'"]],"'])+'"]],["LI",{},["'+messages.readme_cat1_l2+'"]],["LI",{},["'+messages._getKey("readme_cat1_l3", ['",["STRONG",{},["'+messages.fm_unsortedName+'"]],"'])+'",["UL",{},[["LI",{},["'+messages._getKey("readme_cat1_l3_l1", ['",["STRONG",{},["'+messages.fm_rootName+'"]],"'])+'"]],["LI",{},["'+messages.readme_cat1_l3_l2+'"]]]]]],["LI",{},["'+messages._getKey("readme_cat1_l4", ['",["STRONG",{},["'+messages.fm_trashName+'"]],"'])+'",["BR",{},[]]]]]],["P",{},[["BR",{},[]]]],["H2",{},["'+messages.readme_cat2+'",["BR",{},[]]]],["UL",{},[["LI",{},["'+messages._getKey("readme_cat2_l1", ['",["STRONG",{},["'+messages.shareButton+'"]],"', '",["STRONG",{},["'+messages.edit+'"]],"', '",["STRONG",{},["'+messages.view+'"]],"'])+'"]],["LI",{},["'+messages.readme_cat2_l2+'"]]]],["P",{},[["BR",{},[]]]],["H2",{},["'+messages.readme_cat3+'"]],["UL",{},[["LI",{},["'+messages.readme_cat3_l1+'"]],["LI",{},["'+messages.readme_cat3_l2+'"]],["LI",{},["'+messages.readme_cat3_l3+'",["BR",{},[]]]]]]],' +
|
||||
'{"metadata":{"defaultTitle":"' + messages.driveReadmeTitle + '","title":"' + messages.driveReadmeTitle + '"}}]';
|
||||
|
|
|
@ -719,27 +719,15 @@ define([
|
|||
};
|
||||
|
||||
Pages['/settings/'] = Pages['/settings/index.html'] = function () {
|
||||
return [
|
||||
h('div#toolbar'),
|
||||
h('div#container'),
|
||||
loadingScreen()
|
||||
];
|
||||
return loadingScreen();
|
||||
};
|
||||
|
||||
Pages['/profile/'] = Pages['/profile/index.html'] = function () {
|
||||
return [
|
||||
h('div#cp-toolbar'),
|
||||
h('div#container'),
|
||||
loadingScreen()
|
||||
];
|
||||
return loadingScreen();
|
||||
};
|
||||
|
||||
Pages['/todo/'] = Pages['/todo/index.html'] = function () {
|
||||
return [
|
||||
h('div#toolbar'),
|
||||
h('div#container'),
|
||||
loadingScreen()
|
||||
];
|
||||
return loadingScreen();
|
||||
};
|
||||
|
||||
return Pages;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
@rightside-color: @colortheme_sidebar-right-fg;
|
||||
@description-color: @colortheme_sidebar-description;
|
||||
|
||||
@button-width: 400px;
|
||||
@sidebar_button-width: 400px;
|
||||
|
||||
|
||||
.sidebar-layout_main() {
|
||||
|
@ -38,13 +38,13 @@
|
|||
overflow: auto;
|
||||
|
||||
// Following rules are only in settings
|
||||
.element {
|
||||
.cp-sidebarlayout-element {
|
||||
label:not(.noTitle), .label {
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.description {
|
||||
.cp-sidebarlayout-description {
|
||||
display: block;
|
||||
color: @description-color;
|
||||
margin-bottom: 5px;
|
||||
|
@ -56,9 +56,9 @@
|
|||
height: 40px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.inputBlock {
|
||||
.cp-sidebarlayout-input-block {
|
||||
display: inline-flex;
|
||||
width: @button-width;
|
||||
width: @sidebar_button-width;
|
||||
input {
|
||||
flex: 1;
|
||||
border-radius: 0.25em 0 0 0.25em;
|
||||
|
|
|
@ -35,4 +35,5 @@ body.cp-app-poll { @import "../../../poll/app-poll.less"; }
|
|||
body.cp-app-whiteboard { @import "../../../whiteboard/app-whiteboard.less"; }
|
||||
body.cp-app-todo { @import "../../../todo/app-todo.less"; }
|
||||
body.cp-app-profile { @import "../../../profile/app-profile.less"; }
|
||||
body.cp-app-settings { @import "../../../settings/app-settings.less"; }
|
||||
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
define([
|
||||
'jquery',
|
||||
'/customize/messages.js'
|
||||
], function($, Messages) {
|
||||
var LS_LANG = "CRYPTPAD_LANG";
|
||||
|
||||
var Msg = {};
|
||||
|
||||
Msg.getLanguage = Messages._getLanguage;
|
||||
|
||||
// Add handler to the language selector
|
||||
Msg.setLanguage = function (l, sframeChan, cb) {
|
||||
console.log(sframeChan);
|
||||
if (sframeChan) {
|
||||
// We're in the sandbox
|
||||
sframeChan.query("Q_LANGUAGE_SET", l, cb);
|
||||
return;
|
||||
}
|
||||
localStorage.setItem(LS_LANG, l);
|
||||
cb();
|
||||
};
|
||||
|
||||
Msg.initSelector = function ($select, sfcommon) {
|
||||
var selector = $select || $('#cp-language-selector');
|
||||
|
||||
if (!selector.length) { return; }
|
||||
|
||||
var language = Messages._getLanguage();
|
||||
|
||||
// Select the current language in the list
|
||||
selector.setValue(language || 'en');
|
||||
|
||||
// Listen for language change
|
||||
$(selector).find('a.cp-language-value').on('click', function () {
|
||||
var newLanguage = $(this).attr('data-value');
|
||||
Msg.setLanguage(newLanguage, sfcommon && sfcommon.getSframeChannel(), function () {
|
||||
if (newLanguage !== language) {
|
||||
if (sfcommon) {
|
||||
sfcommon.gotoURL();
|
||||
return;
|
||||
}
|
||||
window.location.reload();
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Msg.applyTranslation = function () {
|
||||
var translateText = function (i, e) {
|
||||
var $el = $(e);
|
||||
var key = $el.data('localization');
|
||||
$el.html(Messages[key]);
|
||||
};
|
||||
var translateAppend = function (i, e) {
|
||||
var $el = $(e);
|
||||
var key = $el.data('localization-append');
|
||||
$el.append(Messages[key]);
|
||||
};
|
||||
var translateTitle = function () {
|
||||
var $el = $(this);
|
||||
var key = $el.data('localization-title');
|
||||
$el.attr('title', Messages[key]);
|
||||
};
|
||||
var translatePlaceholder = function () {
|
||||
var $el = $(this);
|
||||
var key = $el.data('localization-placeholder');
|
||||
$el.attr('placeholder', Messages[key]);
|
||||
};
|
||||
$('[data-localization]').each(translateText);
|
||||
$('[data-localization-append]').each(translateAppend);
|
||||
$('[data-localization-title]').each(translateTitle);
|
||||
$('[data-localization-placeholder]').each(translatePlaceholder);
|
||||
$('#pad-iframe').contents().find('[data-localization]').each(translateText);
|
||||
$('#pad-iframe').contents().find('[data-localization-append]').each(translateAppend);
|
||||
$('#pad-iframe').contents().find('[data-localization-title]').each(translateTitle);
|
||||
$('#pad-iframe').contents().find('[data-localization-placeholder]').each(translatePlaceholder);
|
||||
};
|
||||
|
||||
return Msg;
|
||||
|
||||
});
|
|
@ -14,6 +14,8 @@ define([
|
|||
'/common/common-file.js',
|
||||
'/file/file-crypto.js',
|
||||
'/common/common-realtime.js',
|
||||
'/common/common-language.js',
|
||||
|
||||
'/common/clipboard.js',
|
||||
'/common/pinpad.js',
|
||||
'/customize/application_config.js',
|
||||
|
@ -21,8 +23,8 @@ define([
|
|||
'/bower_components/nthen/index.js',
|
||||
'/bower_components/localforage/dist/localforage.min.js',
|
||||
], function ($, Config, Messages, Store, Util, Hash, UI, History, UserList, Title, Metadata,
|
||||
Messaging, Files, FileCrypto, Realtime, Clipboard,
|
||||
Pinpad, AppConfig, MediaTag, Nthen, localForage) {
|
||||
Messaging, Files, FileCrypto, Realtime, Language, Clipboard,
|
||||
Pinpad, AppConfig, MediaTag, Nthen, localForage) {
|
||||
|
||||
// Configure MediaTags to use our local viewer
|
||||
if (MediaTag && MediaTag.PdfPlugin) {
|
||||
|
@ -183,6 +185,9 @@ define([
|
|||
common.getLanguage = function () {
|
||||
return Messages._languageUsed;
|
||||
};
|
||||
common.setLanguage = function (l, cb) {
|
||||
Language.setLanguage(l, null, cb);
|
||||
};
|
||||
common.getUserlist = function () {
|
||||
if (store) {
|
||||
if (store.getProxy() && store.getProxy().info) {
|
||||
|
@ -220,7 +225,12 @@ define([
|
|||
|
||||
common.isFeedbackAllowed = function () {
|
||||
try {
|
||||
if (!getStore().getProxy().proxy.allowUserFeedback) { return false; }
|
||||
var entry = common.find(getProxy(), [
|
||||
'settings',
|
||||
'general',
|
||||
'allowUserFeedback'
|
||||
]);
|
||||
if (!entry) { return false; }
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
@ -494,7 +504,7 @@ define([
|
|||
if (getProxy()) {
|
||||
getProxy()[common.displayNameKey] = value;
|
||||
}
|
||||
if (typeof cb === "function") { cb(); }
|
||||
if (typeof cb === "function") { whenRealtimeSyncs(getRealtime(), cb); }
|
||||
};
|
||||
common.setAttribute = function (attr, value, cb) {
|
||||
getStore().setAttribute(attr, value, function (err, data) {
|
||||
|
@ -522,6 +532,9 @@ define([
|
|||
common.getThumbnail = function (key, cb) {
|
||||
localForage.getItem(key, cb);
|
||||
};
|
||||
common.clearThumbnail = function (cb) {
|
||||
localForage.clear(cb);
|
||||
};
|
||||
|
||||
/* this returns a reference to your proxy. changing it will change your drive.
|
||||
*/
|
||||
|
@ -1896,7 +1909,7 @@ define([
|
|||
$block.appendTo($container);
|
||||
}
|
||||
|
||||
Messages._initSelector($block);
|
||||
Language.initSelector($block);
|
||||
|
||||
return $block;
|
||||
};
|
||||
|
@ -2310,7 +2323,7 @@ define([
|
|||
|
||||
// MAGIC that happens implicitly
|
||||
$(function () {
|
||||
Messages._applyTranslation();
|
||||
Language.applyTranslation();
|
||||
});
|
||||
|
||||
return common;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
define([
|
||||
'/customize/messages.js',
|
||||
], function (Messages) {
|
||||
Messages._applyTranslation();
|
||||
'/common/common-language.js',
|
||||
], function (Language) {
|
||||
Language.applyTranslation();
|
||||
});
|
||||
|
|
|
@ -236,8 +236,11 @@ define([
|
|||
}
|
||||
}
|
||||
|
||||
if (typeof(proxy.allowUserFeedback) !== 'boolean') {
|
||||
proxy.allowUserFeedback = true;
|
||||
if (!proxy.settings || !proxy.settings.general ||
|
||||
typeof(proxy.settings.general.allowUserFeedback) !== 'boolean') {
|
||||
proxy.settings = proxy.settings || {};
|
||||
proxy.settings.general = proxy.settings.general || {};
|
||||
proxy.settings.general.allowUserFeedback = true;
|
||||
}
|
||||
|
||||
if (typeof(proxy.uid) !== 'string' || proxy.uid.length !== 32) {
|
||||
|
|
|
@ -63,5 +63,22 @@ define([], function () {
|
|||
Cryptpad.feedback('Migrate-3', true);
|
||||
userObject.version = version = 3;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Migration 4: allowUserFeedback to settings
|
||||
var migrateLanguage = function () {
|
||||
var settings = userObject.settings = userObject.settings || {};
|
||||
if (typeof(userObject['allowUserFeedback']) !== "undefined") {
|
||||
settings.general = settings.general || {};
|
||||
settings.general.allowUserFeedback = userObject['allowUserFeedback'];
|
||||
delete userObject['allowUserFeedback'];
|
||||
}
|
||||
};
|
||||
if (version < 4) {
|
||||
migrateLanguage();
|
||||
Cryptpad.feedback('Migrate-4', true);
|
||||
userObject.version = version = 4;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
|
@ -3,12 +3,13 @@ define([
|
|||
'/api/config',
|
||||
'/common/cryptpad-common.js',
|
||||
'/common/common-util.js',
|
||||
'/common/common-language.js',
|
||||
'/common/media-tag.js',
|
||||
'/common/tippy.min.js',
|
||||
'/customize/application_config.js',
|
||||
|
||||
'css!/common/tippy.css',
|
||||
], function ($, Config, Cryptpad, Util, MediaTag, Tippy, AppConfig) {
|
||||
], function ($, Config, Cryptpad, Util, Language, MediaTag, Tippy, AppConfig) {
|
||||
var UI = {};
|
||||
var Messages = Cryptpad.Messages;
|
||||
|
||||
|
@ -574,6 +575,43 @@ define([
|
|||
return $userAdmin;
|
||||
};
|
||||
|
||||
// Provide $container if you want to put the generated block in another element
|
||||
// Provide $initBlock if you already have the menu block and you want the content inserted in it
|
||||
UI.createLanguageSelector = function (common, $container, $initBlock) {
|
||||
var options = [];
|
||||
var languages = Messages._languages;
|
||||
var keys = Object.keys(languages).sort();
|
||||
keys.forEach(function (l) {
|
||||
options.push({
|
||||
tag: 'a',
|
||||
attributes: {
|
||||
'class': 'cp-language-value',
|
||||
'data-value': l,
|
||||
'href': '#',
|
||||
},
|
||||
content: languages[l] // Pretty name of the language value
|
||||
});
|
||||
});
|
||||
var dropdownConfig = {
|
||||
text: Messages.language, // Button initial text
|
||||
options: options, // Entries displayed in the menu
|
||||
//left: true, // Open to the left of the button
|
||||
container: $initBlock, // optional
|
||||
isSelect: true
|
||||
};
|
||||
var $block = Cryptpad.createDropdown(dropdownConfig);
|
||||
$block.attr('id', 'cp-language-selector');
|
||||
|
||||
if ($container) {
|
||||
$block.appendTo($container);
|
||||
}
|
||||
|
||||
Language.initSelector($block, common);
|
||||
|
||||
return $block;
|
||||
};
|
||||
|
||||
|
||||
UI.initFilePicker = function (common, cfg) {
|
||||
var onSelect = cfg.onSelect || $.noop;
|
||||
var sframeChan = common.getSframeChannel();
|
||||
|
|
|
@ -118,6 +118,7 @@ define([
|
|||
netfluxId: Cryptpad.getNetwork().webChannels[0].myID,
|
||||
},
|
||||
priv: {
|
||||
edPublic: proxy.edPublic,
|
||||
accountName: Cryptpad.getAccountName(),
|
||||
origin: window.location.origin,
|
||||
pathname: window.location.pathname,
|
||||
|
@ -452,6 +453,10 @@ define([
|
|||
});
|
||||
});
|
||||
|
||||
sframeChan.on('Q_LANGUAGE_SET', function (data, cb) {
|
||||
Cryptpad.setLanguage(data, cb);
|
||||
});
|
||||
|
||||
if (cfg.addRpc) {
|
||||
cfg.addRpc(sframeChan, Cryptpad);
|
||||
}
|
||||
|
|
|
@ -83,6 +83,7 @@ define([
|
|||
funcs.createButton = callWithCommon(UI.createButton);
|
||||
funcs.createUsageBar = callWithCommon(UI.createUsageBar);
|
||||
funcs.updateTags = callWithCommon(UI.updateTags);
|
||||
funcs.createLanguageSelector = callWithCommon(UI.createLanguageSelector);
|
||||
|
||||
// Thumb
|
||||
funcs.displayThumbnail = callWithCommon(Thumb.displayThumbnail);
|
||||
|
|
|
@ -176,4 +176,19 @@ define({
|
|||
// Store outside and get thumbnails inside (stored with localForage (indexedDB) outside)
|
||||
'Q_THUMBNAIL_SET': true,
|
||||
'Q_THUMBNAIL_GET': true,
|
||||
|
||||
// Settings app only
|
||||
// Clear all thumbnails
|
||||
'Q_THUMBNAIL_CLEAR': true,
|
||||
// Backup and restore a drive
|
||||
'Q_SETTINGS_DRIVE_GET': true,
|
||||
'Q_SETTINGS_DRIVE_SET': true,
|
||||
'Q_SETTINGS_DRIVE_RESET': true,
|
||||
// Logout from all the devices where the account is logged in
|
||||
'Q_SETTINGS_LOGOUT': true,
|
||||
// Import pads from this computer's anon session into the current user account
|
||||
'Q_SETTINGS_IMPORT_LOCAL': true,
|
||||
|
||||
// Store the language selected in the iframe into localStorage outside
|
||||
'Q_LANGUAGE_SET': true,
|
||||
});
|
||||
|
|
|
@ -280,8 +280,8 @@ span {
|
|||
.cp-app-drive-tree-category:last-child {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.limit-container {
|
||||
margin-top: 0;
|
||||
.cp-limit-container {
|
||||
margin-top: 5px;
|
||||
}
|
||||
#cp-app-drive-tree-search {
|
||||
text-align: center;
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="cp">
|
||||
<!-- If this file is not called customize.dist/src/template.html, it is generated -->
|
||||
<head>
|
||||
<title data-localization="main_title">CryptPad: Zero Knowledge, Collaborative Real Time Editing</title>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||
<link rel="icon" type="image/png" href="/customize/main-favicon.png" id="favicon"/>
|
||||
<script async data-bootload="/customize/template.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
|
||||
</head>
|
||||
<body class="html">
|
||||
<noscript>
|
||||
<p><strong>OOPS</strong> In order to do encryption in your browser, Javascript is really <strong>really</strong> required.</p>
|
||||
<p><strong>OUPS</strong> Afin de pouvoir réaliser le chiffrement dans votre navigateur, Javascript est <strong>vraiment</strong> nécessaire.</p>
|
||||
</noscript>
|
||||
</html>
|
|
@ -0,0 +1,569 @@
|
|||
define([
|
||||
'jquery',
|
||||
'/common/cryptpad-common.js',
|
||||
'/common/cryptget.js',
|
||||
'/common/mergeDrive.js',
|
||||
'/common/toolbar2.js',
|
||||
'/bower_components/file-saver/FileSaver.min.js',
|
||||
|
||||
'less!/customize/src/less/cryptpad.less',
|
||||
'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
|
||||
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
|
||||
'less!/customize/src/less/toolbar.less',
|
||||
'less!/settings/main.less',
|
||||
], function ($, Cryptpad, Crypt, Merge, Toolbar) {
|
||||
var saveAs = window.saveAs;
|
||||
|
||||
var USERNAME_KEY = 'cryptpad.username';
|
||||
|
||||
var APP = window.APP = {
|
||||
Cryptpad: Cryptpad,
|
||||
_onRefresh: []
|
||||
};
|
||||
|
||||
var Messages = Cryptpad.Messages;
|
||||
|
||||
// Manage changes in the realtime object made from another page
|
||||
var onRefresh = function (h) {
|
||||
if (typeof(h) !== "function") { return; }
|
||||
if (APP._onRefresh.indexOf(h) !== -1) { return; }
|
||||
APP._onRefresh.push(h);
|
||||
};
|
||||
var refresh = APP.refresh = function () {
|
||||
APP._onRefresh.forEach(function (h) {
|
||||
h();
|
||||
});
|
||||
};
|
||||
|
||||
var categories = {
|
||||
'account': [
|
||||
'infoBlock',
|
||||
'displayName',
|
||||
'languageSelector',
|
||||
'logoutEverywhere',
|
||||
'resetTips',
|
||||
'thumbnails',
|
||||
'userFeedback'
|
||||
],
|
||||
'drive': [
|
||||
'backupDrive',
|
||||
'importLocalPads',
|
||||
'resetDrive'
|
||||
],
|
||||
'code': [
|
||||
'indentUnit',
|
||||
'indentType'
|
||||
]
|
||||
};
|
||||
|
||||
var createInfoBlock = function (store) {
|
||||
var obj = store.proxy;
|
||||
var $div = $('<div>', {'class': 'infoBlock'});
|
||||
|
||||
var $account = $('<div>', {'class': 'element'}).appendTo($div);
|
||||
var accountName = obj.login_name || localStorage[Cryptpad.userNameKey];
|
||||
var $label = $('<span>', {'class': 'label'}).text(Messages.user_accountName);
|
||||
var $name = $('<span>').text(accountName || '');
|
||||
if (!accountName) {
|
||||
$label.text('');
|
||||
$name.text(Messages.settings_anonymous);
|
||||
}
|
||||
$account.append($label).append($name);
|
||||
|
||||
var publicKey = obj.edPublic;
|
||||
if (publicKey) {
|
||||
var $key = $('<div>', {'class': 'element'}).appendTo($div);
|
||||
var userHref = Cryptpad.getUserHrefFromKeys(accountName, publicKey);
|
||||
var $pubLabel = $('<span>', {'class': 'label'})
|
||||
.text(Messages.settings_publicSigningKey);
|
||||
$key.append($pubLabel).append(Cryptpad.dialog.selectable(userHref));
|
||||
}
|
||||
|
||||
return $div;
|
||||
};
|
||||
|
||||
// Create the block containing the display name field
|
||||
var createDisplayNameInput = function (store) {
|
||||
var obj = store.proxy;
|
||||
var $div = $('<div>', {'class': 'displayName element'});
|
||||
$('<label>', {'for' : 'displayName'}).text(Messages.user_displayName).appendTo($div);
|
||||
var $inputBlock = $('<div>', {'class': 'inputBlock'}).appendTo($div);
|
||||
var $input = $('<input>', {
|
||||
'type': 'text',
|
||||
'id': 'displayName',
|
||||
'placeholder': Messages.anonymous}).appendTo($inputBlock);
|
||||
var $save = $('<button>', {'class': 'btn btn-primary'}).text(Messages.settings_save).appendTo($inputBlock);
|
||||
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved}).hide().appendTo($div);
|
||||
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'}).hide().appendTo($div);
|
||||
|
||||
var displayName = obj[USERNAME_KEY] || '';
|
||||
$input.val(displayName);
|
||||
|
||||
// When the display name is changed (enter or button clicked)
|
||||
var todo = function () {
|
||||
displayName = $input.val();
|
||||
if (displayName === obj[USERNAME_KEY]) { return; }
|
||||
obj[USERNAME_KEY] = displayName;
|
||||
Cryptpad.changeDisplayName(displayName);
|
||||
$spinner.show();
|
||||
Cryptpad.whenRealtimeSyncs(store.info.realtime, function () {
|
||||
$spinner.hide();
|
||||
$ok.show();
|
||||
});
|
||||
};
|
||||
$input.on('keyup', function (e) {
|
||||
if ($input.val() !== displayName) { $ok.hide(); }
|
||||
if (e.which === 13) { todo(); }
|
||||
});
|
||||
$save.click(todo);
|
||||
|
||||
// On remote change
|
||||
var onChange = function () {
|
||||
if (obj[USERNAME_KEY] !== $input.val()) {
|
||||
$input.val(obj[USERNAME_KEY]);
|
||||
$input.focusout();
|
||||
}
|
||||
};
|
||||
onRefresh(onChange);
|
||||
|
||||
return $div;
|
||||
};
|
||||
var createIndentUnitSelector = function (obj) {
|
||||
var proxy = obj.proxy;
|
||||
proxy.settings = proxy.settings || {};
|
||||
|
||||
var $div = $('<div>', {
|
||||
'class': 'indentUnit element'
|
||||
});
|
||||
$('<label>').text(Messages.settings_codeIndentation).appendTo($div);
|
||||
|
||||
var $inputBlock = $('<div>', {
|
||||
'class': 'inputBlock',
|
||||
}).appendTo($div);
|
||||
|
||||
var $input = $('<input>', {
|
||||
'min': 1,
|
||||
'max': 8,
|
||||
type: 'number',
|
||||
}).on('change', function () {
|
||||
var val = parseInt($input.val());
|
||||
if (typeof(val) !== 'number') { return; }
|
||||
Cryptpad.setAttribute(['codemirror', 'indentUnit'], val);
|
||||
}).appendTo($inputBlock);
|
||||
|
||||
proxy.on('change', [ 'settings', 'codemirror', 'indentUnit', ], function (o, n) {
|
||||
$input.val(n);
|
||||
});
|
||||
|
||||
Cryptpad.getAttribute(['codemirror', 'indentUnit'], function (e, val) {
|
||||
if (e) { return void console.error(e); }
|
||||
if (typeof(val) !== 'number') {
|
||||
$input.val(2);
|
||||
} else {
|
||||
$input.val(val);
|
||||
}
|
||||
});
|
||||
return $div;
|
||||
};
|
||||
|
||||
var createIndentTypeSelector = function (obj) {
|
||||
var proxy = obj.proxy;
|
||||
proxy.settings = proxy.settings || {};
|
||||
|
||||
var key = 'indentWithTabs';
|
||||
|
||||
var $div = $('<div>', {
|
||||
'class': 'indentType element'
|
||||
});
|
||||
$('<label>').text(Messages.settings_codeUseTabs).appendTo($div);
|
||||
|
||||
var $inputBlock = $('<div>', {
|
||||
'class': 'inputBlock',
|
||||
}).css('flex-flow', 'column')
|
||||
.appendTo($div);
|
||||
|
||||
var $input = $('<input>', {
|
||||
type: 'checkbox',
|
||||
}).on('change', function () {
|
||||
var val = $input.is(':checked');
|
||||
if (typeof(val) !== 'boolean') { return; }
|
||||
Cryptpad.setAttribute(['codemirror', key], val);
|
||||
}).appendTo($inputBlock);
|
||||
|
||||
$input[0].checked = !!proxy.settings[key];
|
||||
proxy.on('change', ['settings', 'codemirror', key], function (o, n) {
|
||||
$input[0].checked = !!n;
|
||||
});
|
||||
|
||||
Cryptpad.getAttribute(['codemirror', key], function (e, val) {
|
||||
if (e) { return void console.error(e); }
|
||||
$input[0].checked = !!val;
|
||||
});
|
||||
return $div;
|
||||
};
|
||||
|
||||
var createResetTips = function () {
|
||||
var $div = $('<div>', {'class': 'resetTips element'});
|
||||
$('<label>', {'for' : 'resetTips'}).text(Messages.settings_resetTips).appendTo($div);
|
||||
$('<span>', {'class': 'description'})
|
||||
.text(Messages.settings_resetTipsButton).appendTo($div);
|
||||
var $button = $('<button>', {'id': 'resetTips', 'class': 'btn btn-primary'})
|
||||
.text(Messages.settings_resetTipsAction).appendTo($div);
|
||||
|
||||
$button.click(function () {
|
||||
Object.keys(localStorage).forEach(function (k) {
|
||||
if(k.slice(0, 9) === "hide-info") {
|
||||
delete localStorage[k];
|
||||
}
|
||||
});
|
||||
Cryptpad.alert(Messages.settings_resetTipsDone);
|
||||
});
|
||||
|
||||
return $div;
|
||||
};
|
||||
|
||||
var createThumbnails = function (obj) {
|
||||
var $div = $('<div>', {'class': 'thumbnails element'});
|
||||
$('<label>').text(Messages.settings_thumbnails).appendTo($div);
|
||||
|
||||
// Disable
|
||||
$('<span>', {'class': 'description'})
|
||||
.text(Messages.settings_disableThumbnailsDescription).appendTo($div);
|
||||
var $label = $('<label>', { 'for': 'disableThumbnails', 'class': 'noTitle' })
|
||||
.text(Messages.settings_disableThumbnailsAction);
|
||||
|
||||
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved});
|
||||
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'});
|
||||
|
||||
var $checkbox = $('<input>', {
|
||||
'type': 'checkbox',
|
||||
id: 'disableThumbnails'
|
||||
}).on('change', function () {
|
||||
$spinner.show();
|
||||
$ok.hide();
|
||||
if (!obj.proxy.settings) { obj.proxy.settings = {}; }
|
||||
if (!obj.proxy.settings.general) { obj.proxy.settings.general = {}; }
|
||||
obj.proxy.settings.general.disableThumbnails = $checkbox.is(':checked') || false;
|
||||
Cryptpad.whenRealtimeSyncs(obj.info.realtime, function () {
|
||||
$spinner.hide();
|
||||
$ok.show();
|
||||
});
|
||||
});
|
||||
|
||||
$checkbox.appendTo($div);
|
||||
$label.appendTo($div);
|
||||
|
||||
$ok.hide().appendTo($div);
|
||||
$spinner.hide().appendTo($div);
|
||||
|
||||
if (obj.proxy.settings && obj.proxy.settings.general
|
||||
&& obj.proxy.settings.general.disableThumbnails) {
|
||||
$checkbox[0].checked = true;
|
||||
}
|
||||
|
||||
// Reset
|
||||
/*$('<span>', {'class': 'description'})
|
||||
.text(Messages.settings_resetThumbnailsDescription).appendTo($div);
|
||||
var $button = $('<button>', {'id': 'resetThumbnails', 'class': 'btn btn-primary'})
|
||||
.text(Messages.settings_resetThumbnailsAction).appendTo($div);
|
||||
|
||||
$button.click(function () {
|
||||
// TODO we need to have access to the sandbox localforage first...
|
||||
Cryptpad.alert(Messages.settings_resetThumbnailsDone);
|
||||
});*/
|
||||
|
||||
return $div;
|
||||
};
|
||||
|
||||
var createBackupDrive = function (store) {
|
||||
var obj = store.proxy;
|
||||
var $div = $('<div>', {'class': 'backupDrive element'});
|
||||
|
||||
var exportFile = function () {
|
||||
var sjson = JSON.stringify(obj);
|
||||
var name = obj.login_name || obj[USERNAME_KEY] || Messages.anonymous;
|
||||
var suggestion = name + '-' + new Date().toDateString();
|
||||
Cryptpad.prompt(Cryptpad.Messages.exportPrompt,
|
||||
Cryptpad.fixFileName(suggestion) + '.json', function (filename) {
|
||||
if (!(typeof(filename) === 'string' && filename)) { return; }
|
||||
var blob = new Blob([sjson], {type: "application/json;charset=utf-8"});
|
||||
saveAs(blob, filename);
|
||||
});
|
||||
};
|
||||
var importFile = function (content) {
|
||||
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'}).appendTo($div);
|
||||
Crypt.put(Cryptpad.getUserHash() || localStorage[Cryptpad.fileHashKey], content, function (e) {
|
||||
if (e) { console.error(e); }
|
||||
$spinner.remove();
|
||||
});
|
||||
};
|
||||
|
||||
$('<label>', {'for' : 'exportDrive'}).text(Messages.settings_backupCategory).appendTo($div);
|
||||
$('<span>', {'class': 'description'})
|
||||
.text(Messages.settings_backupTitle).appendTo($div);
|
||||
/* add an export button */
|
||||
var $export = Cryptpad.createButton('export', true, {}, exportFile);
|
||||
$export.attr('class', 'btn btn-success').text(Messages.settings_backup);
|
||||
$div.append($export);
|
||||
|
||||
/* add an import button */
|
||||
var $import = Cryptpad.createButton('import', true, {}, importFile);
|
||||
$import.attr('class', 'btn btn-success').text(Messages.settings_restore);
|
||||
$div.append($import);
|
||||
|
||||
return $div;
|
||||
};
|
||||
|
||||
var createResetDrive = function (obj) {
|
||||
var $div = $('<div>', {'class': 'resetDrive element'});
|
||||
$('<label>', {'for' : 'resetDrive'}).text(Messages.settings_resetNewTitle).appendTo($div);
|
||||
$('<span>', {'class': 'description'})
|
||||
.text(Messages.settings_reset).appendTo($div);
|
||||
var $button = $('<button>', {'id': 'resetDrive', 'class': 'btn btn-danger'})
|
||||
.text(Messages.settings_resetButton).appendTo($div);
|
||||
|
||||
$button.click(function () {
|
||||
Cryptpad.prompt(Messages.settings_resetPrompt, "", function (val) {
|
||||
if (val !== "I love CryptPad") {
|
||||
Cryptpad.alert(Messages.settings_resetError);
|
||||
return;
|
||||
}
|
||||
obj.proxy.drive = Cryptpad.getStore().getEmptyObject();
|
||||
Cryptpad.alert(Messages.settings_resetDone);
|
||||
}, undefined, true);
|
||||
});
|
||||
|
||||
return $div;
|
||||
};
|
||||
|
||||
var createUserFeedbackToggle = function (obj) {
|
||||
var $div = $('<div>', { 'class': 'userFeedback element'});
|
||||
|
||||
$('<span>', {'class': 'label'}).text(Messages.settings_userFeedbackTitle).appendTo($div);
|
||||
|
||||
var $label = $('<label>', { 'for': 'userFeedback', 'class': 'noTitle' })
|
||||
.text(Messages.settings_userFeedback);
|
||||
|
||||
$('<span>', {'class': 'description'})
|
||||
.append(Messages.settings_userFeedbackHint1)
|
||||
.append(Messages.settings_userFeedbackHint2).appendTo($div);
|
||||
|
||||
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved});
|
||||
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'});
|
||||
|
||||
var $checkbox = $('<input>', {
|
||||
'type': 'checkbox',
|
||||
}).on('change', function () {
|
||||
$spinner.show();
|
||||
$ok.hide();
|
||||
obj.proxy.allowUserFeedback = $checkbox.is(':checked') || false;
|
||||
Cryptpad.whenRealtimeSyncs(obj.info.realtime, function () {
|
||||
$spinner.hide();
|
||||
$ok.show();
|
||||
});
|
||||
});
|
||||
|
||||
$checkbox.appendTo($div);
|
||||
$label.appendTo($div);
|
||||
|
||||
$ok.hide().appendTo($div);
|
||||
$spinner.hide().appendTo($div);
|
||||
|
||||
if (obj.proxy.allowUserFeedback) {
|
||||
$checkbox[0].checked = true;
|
||||
}
|
||||
return $div;
|
||||
};
|
||||
|
||||
var createUsageButton = function () {
|
||||
Cryptpad.createUsageBar(function (err, $bar) {
|
||||
if (err) { return void console.error(err); }
|
||||
APP.$usage.html('').append($bar);
|
||||
}, true);
|
||||
};
|
||||
|
||||
var createLogoutEverywhere = function (obj) {
|
||||
var proxy = obj.proxy;
|
||||
var $div = $('<div>', { 'class': 'logoutEverywhere element'});
|
||||
$('<label>', { 'for': 'logoutEverywhere'})
|
||||
.text(Messages.settings_logoutEverywhereTitle).appendTo($div);
|
||||
$('<span>', {'class': 'description'})
|
||||
.text(Messages.settings_logoutEverywhere).appendTo($div);
|
||||
var $button = $('<button>', { id: 'logoutEverywhere', 'class': 'btn btn-primary' })
|
||||
.text(Messages.settings_logoutEverywhereButton)
|
||||
.appendTo($div);
|
||||
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved}).hide().appendTo($div);
|
||||
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'}).hide().appendTo($div);
|
||||
|
||||
$button.click(function () {
|
||||
var realtime = obj.info.realtime;
|
||||
console.log(realtime);
|
||||
|
||||
Cryptpad.confirm(Messages.settings_logoutEverywhereConfirm, function (yes) {
|
||||
if (!yes) { return; }
|
||||
$spinner.show();
|
||||
$ok.hide();
|
||||
|
||||
var token = Math.floor(Math.random()*Number.MAX_SAFE_INTEGER);
|
||||
localStorage.setItem('loginToken', token);
|
||||
proxy.loginToken = token;
|
||||
|
||||
Cryptpad.whenRealtimeSyncs(realtime, function () {
|
||||
$spinner.hide();
|
||||
$ok.show();
|
||||
window.setTimeout(function () {
|
||||
$ok.fadeOut(1500);
|
||||
}, 2500);
|
||||
});
|
||||
});
|
||||
});
|
||||
return $div;
|
||||
};
|
||||
|
||||
var createImportLocalPads = function (obj) {
|
||||
if (!Cryptpad.isLoggedIn()) { return; }
|
||||
var $div = $('<div>', {'class': 'importLocalPads element'});
|
||||
$('<label>', {'for' : 'importLocalPads'}).text(Messages.settings_import).appendTo($div);
|
||||
$('<span>', {'class': 'description'})
|
||||
.text(Messages.settings_importTitle).appendTo($div);
|
||||
var $button = $('<button>', {'id': 'importLocalPads', 'class': 'btn btn-primary'})
|
||||
.text(Messages.settings_import).appendTo($div);
|
||||
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved}).hide().appendTo($div);
|
||||
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'}).hide().appendTo($div);
|
||||
|
||||
$button.click(function () {
|
||||
Cryptpad.confirm(Messages.settings_importConfirm, function (yes) {
|
||||
if (!yes) { return; }
|
||||
$spinner.show();
|
||||
$ok.hide();
|
||||
Merge.anonDriveIntoUser(obj, localStorage.FS_hash, function () {
|
||||
$spinner.hide();
|
||||
$ok.show();
|
||||
Cryptpad.alert(Messages.settings_importDone);
|
||||
});
|
||||
}, undefined, true);
|
||||
});
|
||||
|
||||
return $div;
|
||||
};
|
||||
|
||||
var createLanguageSelector = function () {
|
||||
var $div = $('<div>', {'class': 'languageSelector element'});
|
||||
$('<label>').text(Messages.language).appendTo($div);
|
||||
var $b = Cryptpad.createLanguageSelector().appendTo($div);
|
||||
$b.find('button').addClass('btn btn-secondary');
|
||||
return $div;
|
||||
};
|
||||
|
||||
|
||||
var hideCategories = function () {
|
||||
APP.$rightside.find('> div').hide();
|
||||
};
|
||||
var showCategories = function (cat) {
|
||||
hideCategories();
|
||||
cat.forEach(function (c) {
|
||||
APP.$rightside.find('.'+c).show();
|
||||
});
|
||||
};
|
||||
|
||||
var createLeftside = function () {
|
||||
var $categories = $('<div>', {'class': 'categories'}).appendTo(APP.$leftside);
|
||||
APP.$usage = $('<div>', {'class': 'usage'}).appendTo(APP.$leftside);
|
||||
var active = 'account';
|
||||
Object.keys(categories).forEach(function (key) {
|
||||
var $category = $('<div>', {'class': 'category'}).appendTo($categories);
|
||||
if (key === 'account') { $category.append($('<span>', {'class': 'fa fa-user-o'})); }
|
||||
if (key === 'drive') { $category.append($('<span>', {'class': 'fa fa-hdd-o'})); }
|
||||
if (key === 'code') { $category.append($('<span>', {'class': 'fa fa-file-code-o' })); }
|
||||
|
||||
if (key === active) {
|
||||
$category.addClass('active');
|
||||
}
|
||||
|
||||
$category.click(function () {
|
||||
active = key;
|
||||
$categories.find('.active').removeClass('active');
|
||||
$category.addClass('active');
|
||||
showCategories(categories[key]);
|
||||
});
|
||||
|
||||
$category.append(Messages['settings_cat_'+key]);
|
||||
});
|
||||
showCategories(categories[active]);
|
||||
};
|
||||
|
||||
var createToolbar = function () {
|
||||
var displayed = ['useradmin', 'newpad', 'limit', 'upgrade', 'pageTitle'];
|
||||
var configTb = {
|
||||
displayed: displayed,
|
||||
ifrw: window,
|
||||
common: Cryptpad,
|
||||
$container: APP.$toolbar,
|
||||
pageTitle: Messages.settings_title
|
||||
};
|
||||
var toolbar = APP.toolbar = Toolbar.create(configTb);
|
||||
toolbar.$rightside.html(''); // Remove the drawer if we don't use it to hide the toolbar
|
||||
};
|
||||
|
||||
var andThen = function (obj) {
|
||||
APP.$leftside = $('<div>', {id: 'leftSide'}).appendTo(APP.$container);
|
||||
var $rightside = APP.$rightside = $('<div>', {id: 'rightSide'}).appendTo(APP.$container);
|
||||
|
||||
createToolbar();
|
||||
|
||||
//$rightside.append(createTitle());
|
||||
$rightside.append(createInfoBlock(obj));
|
||||
$rightside.append(createDisplayNameInput(obj));
|
||||
$rightside.append(createLanguageSelector());
|
||||
$rightside.append(createIndentUnitSelector(obj));
|
||||
$rightside.append(createIndentTypeSelector(obj));
|
||||
|
||||
if (Cryptpad.isLoggedIn()) {
|
||||
$rightside.append(createLogoutEverywhere(obj));
|
||||
}
|
||||
$rightside.append(createResetTips());
|
||||
$rightside.append(createThumbnails(obj));
|
||||
$rightside.append(createBackupDrive(obj));
|
||||
$rightside.append(createImportLocalPads(obj));
|
||||
$rightside.append(createResetDrive(obj));
|
||||
$rightside.append(createUserFeedbackToggle(obj));
|
||||
|
||||
obj.proxy.on('change', [], refresh);
|
||||
obj.proxy.on('remove', [], refresh);
|
||||
Cryptpad.onDisplayNameChanged(refresh);
|
||||
|
||||
createLeftside();
|
||||
createUsageButton();
|
||||
|
||||
Cryptpad.removeLoadingScreen();
|
||||
};
|
||||
|
||||
$(function () {
|
||||
$(window).click(function () {
|
||||
$('.cp-dropdown-content').hide();
|
||||
});
|
||||
|
||||
APP.$container = $('#container');
|
||||
APP.$toolbar = $('#toolbar');
|
||||
|
||||
Cryptpad.ready(function () {
|
||||
//if (!Cryptpad.getUserHash()) { return redirectToMain(); }
|
||||
|
||||
var storeObj = Cryptpad.getStore().getProxy && Cryptpad.getStore().getProxy().proxy
|
||||
? Cryptpad.getStore().getProxy() : undefined;
|
||||
|
||||
andThen(storeObj);
|
||||
Cryptpad.reportAppUsage();
|
||||
});
|
||||
});
|
||||
|
||||
window.addEventListener('storage', function (e) {
|
||||
if (e.key !== Cryptpad.userHashKey) { return; }
|
||||
var o = e.oldValue;
|
||||
var n = e.newValue;
|
||||
window.location.reload();
|
||||
if (o && !n) { // disconnect
|
||||
//redirectToMain();
|
||||
}
|
||||
});
|
||||
});
|
|
@ -3,8 +3,6 @@
|
|||
@import (once) "../../customize/src/less2/include/markdown.less";
|
||||
@import (once) '../../customize/src/less2/include/fileupload.less';
|
||||
@import (once) '../../customize/src/less2/include/alertify.less';
|
||||
//@import (once) '../../customize/src/less/mixins.less';
|
||||
//@import (once) '../../customize/src/less/variables.less";
|
||||
|
||||
@import (once) '../../customize/src/less2/include/avatar.less';
|
||||
@import (once) '../../customize/src/less2/include/sidebar-layout.less';
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
@import (once) "../../customize/src/less2/include/colortheme.less";
|
||||
@import (once) "../../customize/src/less2/include/browser.less";
|
||||
@import (once) "../../customize/src/less2/include/toolbar.less";
|
||||
@import (once) "../../customize/src/less2/include/markdown.less";
|
||||
@import (once) '../../customize/src/less2/include/alertify.less';
|
||||
@import (once) '../../customize/src/less2/include/sidebar-layout.less';
|
||||
@import (once) "../../customize/src/less2/include/limit-bar.less";
|
||||
|
||||
.toolbar_main();
|
||||
.alertify_main();
|
||||
.sidebar-layout_main();
|
||||
.limit-bar_main();
|
||||
|
||||
// body
|
||||
&.cp-app-settings {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
#cp-sidebarlayout-container {
|
||||
#cp-sidebarlayout-rightside {
|
||||
.cp-settings-userfeedback, .cp-settings-thumbnails {
|
||||
input[type="checkbox"] {
|
||||
vertical-align: middle;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
.cp-settings-language-selector {
|
||||
button.btn {
|
||||
width: @sidebar_button-width;
|
||||
background-color: @colortheme_sidebar-button-alt-bg;
|
||||
border-color: #adadad;
|
||||
color: black;
|
||||
&:hover {
|
||||
background-color: darken(@colortheme_sidebar-button-alt-bg, 15%);
|
||||
}
|
||||
}
|
||||
}
|
||||
.cp-sidebarlayout-input-block {
|
||||
input {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
padding: 5px;
|
||||
padding-left: 15px;
|
||||
&[type="number"] {
|
||||
border-right: 1px solid #adadad;
|
||||
}
|
||||
&[type="checkbox"] {
|
||||
margin-right: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
.cp-settings-info-block {
|
||||
[type="text"] {
|
||||
width: @sidebar_button-width;
|
||||
}
|
||||
}
|
||||
.cp-settings-backup-drive {
|
||||
button {
|
||||
span.fa {
|
||||
margin-right: 5px;
|
||||
}
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +1,38 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="cp">
|
||||
<!-- If this file is not called customize.dist/src/template.html, it is generated -->
|
||||
<html>
|
||||
<head>
|
||||
<title data-localization="main_title">CryptPad: Zero Knowledge, Collaborative Real Time Editing</title>
|
||||
<title>CryptPad</title>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||
<link rel="icon" type="image/png" href="/customize/main-favicon.png" id="favicon"/>
|
||||
<script async data-bootload="/customize/template.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="referrer" content="no-referrer" />
|
||||
<script async data-bootload="main.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
|
||||
<style>
|
||||
html, body {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
#sbox-iframe {
|
||||
position:fixed;
|
||||
top:0px;
|
||||
left:0px;
|
||||
bottom:0px;
|
||||
right:0px;
|
||||
width:100%;
|
||||
height:100%;
|
||||
border:none;
|
||||
margin:0;
|
||||
padding:0;
|
||||
overflow:hidden;
|
||||
}
|
||||
#sbox-filePicker-iframe {
|
||||
position: fixed;
|
||||
top:0; left:0;
|
||||
bottom:0; right:0;
|
||||
width:100%;
|
||||
height: 100%;
|
||||
border: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="html">
|
||||
<noscript>
|
||||
<p><strong>OOPS</strong> In order to do encryption in your browser, Javascript is really <strong>really</strong> required.</p>
|
||||
<p><strong>OUPS</strong> Afin de pouvoir réaliser le chiffrement dans votre navigateur, Javascript est <strong>vraiment</strong> nécessaire.</p>
|
||||
</noscript>
|
||||
</html>
|
||||
<body>
|
||||
<iframe id="sbox-iframe">
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="cp-app-noscroll">
|
||||
<head>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
||||
<script async data-bootload="/settings/inner.js" data-main="/common/sframe-boot.js?ver=1.4" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
|
||||
<style>
|
||||
.loading-hidden { display: none; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="cp-app-settings">
|
||||
<div id="cp-toolbar" class="cp-toolbar-container"></div>
|
||||
<div id="cp-sidebarlayout-container"></div>
|
||||
<noscript>
|
||||
<p><strong>OOPS</strong> In order to do encryption in your browser, Javascript is really <strong>really</strong> required.</p>
|
||||
<p><strong>OUPS</strong> Afin de pouvoir réaliser le chiffrement dans votre navigateur, Javascript est <strong>vraiment</strong> nécessaire.</p>
|
||||
</noscript>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,551 @@
|
|||
define([
|
||||
'jquery',
|
||||
'/common/toolbar3.js',
|
||||
'/common/cryptpad-common.js',
|
||||
'/bower_components/nthen/index.js',
|
||||
'/common/sframe-common.js',
|
||||
'/customize/pages.js',
|
||||
|
||||
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
|
||||
'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
|
||||
'less!/customize/src/less2/main.less',
|
||||
], function (
|
||||
$,
|
||||
Toolbar,
|
||||
Cryptpad,
|
||||
nThen,
|
||||
SFCommon,
|
||||
Pages
|
||||
)
|
||||
{
|
||||
var Messages = Cryptpad.Messages;
|
||||
var APP = window.APP = {
|
||||
Cryptpad: Cryptpad,
|
||||
};
|
||||
var onConnectError = function () {
|
||||
Cryptpad.errorLoadingScreen(Messages.websocketError);
|
||||
};
|
||||
|
||||
var common;
|
||||
var metadataMgr;
|
||||
var privateData;
|
||||
var sframeChan;
|
||||
|
||||
var categories = {
|
||||
'account': [
|
||||
'cp-settings-info-block',
|
||||
'cp-settings-displayname',
|
||||
'cp-settings-language-selector',
|
||||
'cp-settings-logout-everywhere',
|
||||
'cp-settings-resettips',
|
||||
'cp-settings-thumbnails',
|
||||
'cp-settings-userfeedback'
|
||||
],
|
||||
'drive': [
|
||||
'cp-settings-backup-drive',
|
||||
'cp-settings-import-local-pads',
|
||||
'cp-settings-reset-drive'
|
||||
],
|
||||
'code': [
|
||||
'cp-settings-indent-unit',
|
||||
'cp-settings-indent-type'
|
||||
]
|
||||
};
|
||||
|
||||
var createInfoBlock = function () {
|
||||
var $div = $('<div>', {'class': 'cp-settings-info-block'});
|
||||
|
||||
var $account = $('<div>', {'class': 'cp-sidebarlayout-element'}).appendTo($div);
|
||||
var accountName = privateData.accountName;
|
||||
var $label = $('<span>', {'class': 'label'}).text(Messages.user_accountName);
|
||||
var $name = $('<span>').text(accountName || '');
|
||||
if (!accountName) {
|
||||
$label.text('');
|
||||
$name.text(Messages.settings_anonymous);
|
||||
}
|
||||
$account.append($label).append($name);
|
||||
|
||||
var publicKey = privateData.edPublic;
|
||||
if (publicKey) {
|
||||
var $key = $('<div>', {'class': 'cp-sidebarlayout-element'}).appendTo($div);
|
||||
var userHref = Cryptpad.getUserHrefFromKeys(accountName, publicKey);
|
||||
var $pubLabel = $('<span>', {'class': 'label'})
|
||||
.text(Messages.settings_publicSigningKey);
|
||||
$key.append($pubLabel).append(Cryptpad.dialog.selectable(userHref));
|
||||
}
|
||||
|
||||
return $div;
|
||||
};
|
||||
|
||||
// Create the block containing the display name field
|
||||
var createDisplayNameInput = function () {
|
||||
var $div = $('<div>', {'class': 'cp-settings-displayname cp-sidebarlayout-element'});
|
||||
$('<label>', {'for' : 'cp-settings-displayname'}).text(Messages.user_displayName).appendTo($div);
|
||||
var $inputBlock = $('<div>', {'class': 'cp-sidebarlayout-input-block'}).appendTo($div);
|
||||
var $input = $('<input>', {
|
||||
'type': 'text',
|
||||
'id': 'cp-settings-displayname',
|
||||
'placeholder': Messages.anonymous}).appendTo($inputBlock);
|
||||
var $save = $('<button>', {'class': 'btn btn-primary'}).text(Messages.settings_save).appendTo($inputBlock);
|
||||
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved}).hide().appendTo($div);
|
||||
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'}).hide().appendTo($div);
|
||||
|
||||
var displayName = metadataMgr.getUserData().name || '';
|
||||
$input.val(displayName);
|
||||
|
||||
// When the display name is changed (enter or button clicked)
|
||||
var todo = function () {
|
||||
displayName = $input.val();
|
||||
if (displayName === metadataMgr.getUserData().name) { return; }
|
||||
$spinner.show();
|
||||
common.setDisplayName(displayName, function () {
|
||||
$spinner.hide();
|
||||
$ok.show();
|
||||
});
|
||||
};
|
||||
$input.on('keyup', function (e) {
|
||||
if ($input.val() !== displayName) { $ok.hide(); }
|
||||
if (e.which === 13) { todo(); }
|
||||
});
|
||||
$save.click(todo);
|
||||
|
||||
// On remote change
|
||||
var onChange = function () {
|
||||
if (metadataMgr.getUserData().name !== $input.val()) {
|
||||
$input.val(metadataMgr.getUserData().name);
|
||||
$input.focusout();
|
||||
}
|
||||
};
|
||||
metadataMgr.onChange(onChange);
|
||||
|
||||
return $div;
|
||||
};
|
||||
var createIndentUnitSelector = function () {
|
||||
var $div = $('<div>', {
|
||||
'class': 'cp-settings-indent-unit cp-sidebarlayout-element'
|
||||
});
|
||||
$('<label>').text(Messages.settings_codeIndentation).appendTo($div);
|
||||
|
||||
var $inputBlock = $('<div>', {
|
||||
'class': 'cp-sidebarlayout-input-block',
|
||||
}).appendTo($div);
|
||||
|
||||
var $input = $('<input>', {
|
||||
'min': 1,
|
||||
'max': 8,
|
||||
type: 'number',
|
||||
}).on('change', function () {
|
||||
var val = parseInt($input.val());
|
||||
if (typeof(val) !== 'number') { return; }
|
||||
common.setAttribute(['codemirror', 'indentUnit'], val);
|
||||
}).appendTo($inputBlock);
|
||||
|
||||
common.getAttribute(['codemirror', 'indentUnit'], function (e, val) {
|
||||
if (e) { return void console.error(e); }
|
||||
if (typeof(val) !== 'number') {
|
||||
$input.val(2);
|
||||
} else {
|
||||
$input.val(val);
|
||||
}
|
||||
});
|
||||
return $div;
|
||||
};
|
||||
|
||||
var createIndentTypeSelector = function (obj) {
|
||||
var key = 'indentWithTabs';
|
||||
|
||||
var $div = $('<div>', {
|
||||
'class': 'cp-settings-indent-type cp-sidebarlayout-element'
|
||||
});
|
||||
$('<label>').text(Messages.settings_codeUseTabs).appendTo($div);
|
||||
|
||||
var $inputBlock = $('<div>', {
|
||||
'class': 'cp-sidebarlayout-input-block',
|
||||
}).css('flex-flow', 'column')
|
||||
.appendTo($div);
|
||||
|
||||
var $input = $('<input>', {
|
||||
type: 'checkbox',
|
||||
}).on('change', function () {
|
||||
var val = $input.is(':checked');
|
||||
if (typeof(val) !== 'boolean') { return; }
|
||||
common.setAttribute(['codemirror', key], val);
|
||||
}).appendTo($inputBlock);
|
||||
|
||||
/*proxy.on('change', ['settings', 'codemirror', key], function (o, n) {
|
||||
$input[0].checked = !!n;
|
||||
});*/
|
||||
|
||||
common.getAttribute(['codemirror', key], function (e, val) {
|
||||
if (e) { return void console.error(e); }
|
||||
$input[0].checked = !!val;
|
||||
});
|
||||
return $div;
|
||||
};
|
||||
|
||||
var createResetTips = function () {
|
||||
var $div = $('<div>', {'class': 'cp-settings-resettips cp-sidebarlayout-element'});
|
||||
$('<label>').text(Messages.settings_resetTips).appendTo($div);
|
||||
$('<span>', {'class': 'cp-sidebarlayout-description'})
|
||||
.text(Messages.settings_resetTipsButton).appendTo($div);
|
||||
var $button = $('<button>', {'id': 'cp-settings-resettips', 'class': 'btn btn-primary'})
|
||||
.text(Messages.settings_resetTipsAction).appendTo($div);
|
||||
|
||||
var localStore = window.cryptpadStore;
|
||||
$button.click(function () {
|
||||
Object.keys(localStore).forEach(function (k) {
|
||||
if(k.slice(0, 9) === "hide-info") {
|
||||
localStore.put(k, undefined);
|
||||
}
|
||||
});
|
||||
Cryptpad.alert(Messages.settings_resetTipsDone);
|
||||
});
|
||||
|
||||
return $div;
|
||||
};
|
||||
|
||||
var createThumbnails = function () {
|
||||
var $div = $('<div>', {'class': 'cp-settings-thumbnails cp-sidebarlayout-element'});
|
||||
$('<label>').text(Messages.settings_thumbnails).appendTo($div);
|
||||
|
||||
// Disable
|
||||
$('<span>', {'class': 'cp-sidebarlayout-description'})
|
||||
.text(Messages.settings_disableThumbnailsDescription).appendTo($div);
|
||||
var $label = $('<label>', { 'for': 'disableThumbnails', 'class': 'noTitle' })
|
||||
.text(Messages.settings_disableThumbnailsAction);
|
||||
|
||||
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved});
|
||||
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'});
|
||||
|
||||
var $checkbox = $('<input>', {
|
||||
'type': 'checkbox',
|
||||
id: 'disableThumbnails'
|
||||
}).on('change', function () {
|
||||
$spinner.show();
|
||||
$ok.hide();
|
||||
var val = $checkbox.is(':checked') || false;
|
||||
common.setAttribute(['general', 'disableThumbnails'], val, function () {
|
||||
$spinner.hide();
|
||||
$ok.show();
|
||||
});
|
||||
});
|
||||
|
||||
$checkbox.appendTo($div);
|
||||
$label.appendTo($div);
|
||||
|
||||
$ok.hide().appendTo($div);
|
||||
$spinner.hide().appendTo($div);
|
||||
|
||||
common.getAttribute(['general', 'disableThumbnails'], function (e, val) {
|
||||
$checkbox[0].checked = val;
|
||||
});
|
||||
|
||||
// Reset
|
||||
$('<span>', {'class': 'cp-sidebarlayout-description'})
|
||||
.text(Messages.settings_resetThumbnailsDescription).appendTo($div);
|
||||
var $button = $('<button>', {'id': 'resetThumbnails', 'class': 'btn btn-primary'})
|
||||
.text(Messages.settings_resetThumbnailsAction).appendTo($div);
|
||||
|
||||
$button.click(function () {
|
||||
sframeChan.query("Q_THUMBNAIL_CLEAR", null, function (err) {
|
||||
if (err) { return void console.error("Cannot clear localForage"); }
|
||||
Cryptpad.alert(Messages.settings_resetThumbnailsDone);
|
||||
});
|
||||
});
|
||||
|
||||
return $div;
|
||||
};
|
||||
|
||||
var createBackupDrive = function () {
|
||||
var $div = $('<div>', {'class': 'cp-settings-backup-drive cp-sidebarlayout-element'});
|
||||
|
||||
var accountName = privateData.accountName;
|
||||
var displayName = metadataMgr.getUserData().name || '';
|
||||
|
||||
var exportFile = function () {
|
||||
sframeChan.query("Q_SETTINGS_DRIVE_GET", null, function (err, data) {
|
||||
if (err) { return void console.error(err); }
|
||||
var sjson = JSON.stringify(data);
|
||||
var name = displayName || accountName || Messages.anonymous;
|
||||
var suggestion = name + '-' + new Date().toDateString();
|
||||
Cryptpad.prompt(Cryptpad.Messages.exportPrompt,
|
||||
Cryptpad.fixFileName(suggestion) + '.json', function (filename) {
|
||||
if (!(typeof(filename) === 'string' && filename)) { return; }
|
||||
var blob = new Blob([sjson], {type: "application/json;charset=utf-8"});
|
||||
saveAs(blob, filename);
|
||||
});
|
||||
});
|
||||
};
|
||||
var importFile = function (content) {
|
||||
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'}).appendTo($div);
|
||||
try {
|
||||
var data = JSON.parse(content);
|
||||
sframeChan.query("Q_SETTINGS_DRIVE_SET", data, function (e) {
|
||||
if (e) { console.error(e); }
|
||||
$spinner.remove();
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
|
||||
$('<label>', {'for' : 'exportDrive'}).text(Messages.settings_backupCategory).appendTo($div);
|
||||
$('<span>', {'class': 'cp-sidebarlayout-description'})
|
||||
.text(Messages.settings_backupTitle).appendTo($div);
|
||||
/* add an export button */
|
||||
var $export = common.createButton('export', true, {}, exportFile);
|
||||
$export.attr('class', 'btn btn-success').text(Messages.settings_backup);
|
||||
$div.append($export);
|
||||
|
||||
/* add an import button */
|
||||
var $import = common.createButton('import', true, {}, importFile);
|
||||
$import.attr('class', 'btn btn-success').text(Messages.settings_restore);
|
||||
$div.append($import);
|
||||
|
||||
return $div;
|
||||
};
|
||||
|
||||
var createResetDrive = function (obj) {
|
||||
var $div = $('<div>', {'class': 'cp-settings-reset-drive cp-sidebarlayout-element'});
|
||||
$('<label>').text(Messages.settings_resetNewTitle).appendTo($div);
|
||||
$('<span>', {'class': 'cp-sidebarlayout-description'})
|
||||
.text(Messages.settings_reset).appendTo($div);
|
||||
var $button = $('<button>', {'id': 'cp-settings-reset-drive', 'class': 'btn btn-danger'})
|
||||
.text(Messages.settings_resetButton).appendTo($div);
|
||||
|
||||
$button.click(function () {
|
||||
Cryptpad.prompt(Messages.settings_resetPrompt, "", function (val) {
|
||||
if (val !== "I love CryptPad") {
|
||||
Cryptpad.alert(Messages.settings_resetError);
|
||||
return;
|
||||
}
|
||||
sframeChan.query("Q_SETTINGS_DRIVE_RESET", null, function (err) {
|
||||
if (err) { return void console.error(err); }
|
||||
Cryptpad.alert(Messages.settings_resetDone);
|
||||
});
|
||||
}, undefined, true);
|
||||
});
|
||||
|
||||
return $div;
|
||||
};
|
||||
|
||||
var createUserFeedbackToggle = function () {
|
||||
var $div = $('<div>', { 'class': 'cp-settings-userfeedback cp-sidebarlayout-element'});
|
||||
|
||||
$('<span>', {'class': 'label'}).text(Messages.settings_userFeedbackTitle).appendTo($div);
|
||||
|
||||
var $label = $('<label>', { 'for': 'cp-settings-userfeedback', 'class': 'noTitle' })
|
||||
.text(Messages.settings_userFeedback);
|
||||
|
||||
$('<span>', {'class': 'cp-sidebarlayout-description'})
|
||||
.append(Messages.settings_userFeedbackHint1)
|
||||
.append(Messages.settings_userFeedbackHint2).appendTo($div);
|
||||
|
||||
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved});
|
||||
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'});
|
||||
|
||||
var $checkbox = $('<input>', {
|
||||
'type': 'checkbox',
|
||||
id: 'cp-settings-userfeedback'
|
||||
}).on('change', function () {
|
||||
$spinner.show();
|
||||
$ok.hide();
|
||||
var val = $checkbox.is(':checked') || false;
|
||||
common.setAttribute(['general', 'allowUserFeedback'], val, function () {
|
||||
$spinner.hide();
|
||||
$ok.show();
|
||||
});
|
||||
});
|
||||
|
||||
$checkbox.appendTo($div);
|
||||
$label.appendTo($div);
|
||||
|
||||
$ok.hide().appendTo($div);
|
||||
$spinner.hide().appendTo($div);
|
||||
|
||||
if (privateData.feedbackAllowed) {
|
||||
$checkbox[0].checked = true;
|
||||
}
|
||||
return $div;
|
||||
};
|
||||
|
||||
var createUsageButton = function () {
|
||||
common.createUsageBar(function (err, $bar) {
|
||||
if (err) { return void console.error(err); }
|
||||
APP.$usage.html('').append($bar);
|
||||
}, true);
|
||||
};
|
||||
|
||||
var createLogoutEverywhere = function () {
|
||||
var $div = $('<div>', { 'class': 'cp-settings-logout-everywhere cp-sidebarlayout-element'});
|
||||
$('<label>', { 'for': 'cp-settings-logout-everywhere'})
|
||||
.text(Messages.settings_logoutEverywhereTitle).appendTo($div);
|
||||
$('<span>', {'class': 'cp-sidebarlayout-description'})
|
||||
.text(Messages.settings_logoutEverywhere).appendTo($div);
|
||||
var $button = $('<button>', {
|
||||
id: 'cp-settings-logout-everywhere',
|
||||
'class': 'btn btn-primary'
|
||||
}).text(Messages.settings_logoutEverywhereButton)
|
||||
.appendTo($div);
|
||||
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved}).hide().appendTo($div);
|
||||
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'}).hide().appendTo($div);
|
||||
|
||||
$button.click(function () {
|
||||
|
||||
Cryptpad.confirm(Messages.settings_logoutEverywhereConfirm, function (yes) {
|
||||
if (!yes) { return; }
|
||||
$spinner.show();
|
||||
$ok.hide();
|
||||
|
||||
sframeChan.query('Q_SETTINGS_LOGOUT', null, function (err) {
|
||||
$spinner.hide();
|
||||
$ok.show();
|
||||
window.setTimeout(function () {
|
||||
$ok.fadeOut(1500);
|
||||
}, 2500);
|
||||
});
|
||||
});
|
||||
});
|
||||
return $div;
|
||||
};
|
||||
|
||||
var createImportLocalPads = function () {
|
||||
if (!common.isLoggedIn()) { return; }
|
||||
var $div = $('<div>', {'class': 'cp-settings-import-local-pads cp-sidebarlayout-element'});
|
||||
$('<label>', {'for' : 'cp-settings-import-local-pads'})
|
||||
.text(Messages.settings_import).appendTo($div);
|
||||
$('<span>', {'class': 'cp-sidebarlayout-description'})
|
||||
.text(Messages.settings_importTitle).appendTo($div);
|
||||
var $button = $('<button>', {
|
||||
'id': 'cp-settings-import-local-pads',
|
||||
'class': 'btn btn-primary'
|
||||
}).text(Messages.settings_import).appendTo($div);
|
||||
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved}).hide().appendTo($div);
|
||||
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'}).hide().appendTo($div);
|
||||
|
||||
$button.click(function () {
|
||||
Cryptpad.confirm(Messages.settings_importConfirm, function (yes) {
|
||||
if (!yes) { return; }
|
||||
$spinner.show();
|
||||
$ok.hide();
|
||||
sframeChan.query('Q_SETTINGS_IMPORT_LOCAL', null, function (err) {
|
||||
$spinner.hide();
|
||||
$ok.show();
|
||||
Cryptpad.alert(Messages.settings_importDone);
|
||||
});
|
||||
}, undefined, true);
|
||||
});
|
||||
|
||||
return $div;
|
||||
};
|
||||
|
||||
var createLanguageSelector = function () {
|
||||
var $div = $('<div>', {'class': 'cp-settings-language-selector cp-sidebarlayout-element'});
|
||||
$('<label>').text(Messages.language).appendTo($div);
|
||||
var $b = common.createLanguageSelector($div);
|
||||
$b.find('button').addClass('btn btn-secondary');
|
||||
return $div;
|
||||
};
|
||||
|
||||
|
||||
var hideCategories = function () {
|
||||
APP.$rightside.find('> div').hide();
|
||||
};
|
||||
var showCategories = function (cat) {
|
||||
hideCategories();
|
||||
cat.forEach(function (c) {
|
||||
APP.$rightside.find('.'+c).show();
|
||||
});
|
||||
};
|
||||
|
||||
var createLeftside = function () {
|
||||
var $categories = $('<div>', {'class': 'cp-sidebarlayout-categories'})
|
||||
.appendTo(APP.$leftside);
|
||||
APP.$usage = $('<div>', {'class': 'usage'}).appendTo(APP.$leftside);
|
||||
var active = 'account';
|
||||
Object.keys(categories).forEach(function (key) {
|
||||
var $category = $('<div>', {'class': 'cp-sidebarlayout-category'}).appendTo($categories);
|
||||
if (key === 'account') { $category.append($('<span>', {'class': 'fa fa-user-o'})); }
|
||||
if (key === 'drive') { $category.append($('<span>', {'class': 'fa fa-hdd-o'})); }
|
||||
if (key === 'code') { $category.append($('<span>', {'class': 'fa fa-file-code-o' })); }
|
||||
|
||||
if (key === active) {
|
||||
$category.addClass('cp-leftside-active');
|
||||
}
|
||||
|
||||
$category.click(function () {
|
||||
active = key;
|
||||
$categories.find('.cp-leftside-active').removeClass('cp-leftside-active');
|
||||
$category.addClass('cp-leftside-active');
|
||||
showCategories(categories[key]);
|
||||
});
|
||||
|
||||
$category.append(Messages['settings_cat_'+key]);
|
||||
});
|
||||
showCategories(categories[active]);
|
||||
};
|
||||
|
||||
|
||||
|
||||
nThen(function (waitFor) {
|
||||
$(waitFor(Cryptpad.addLoadingScreen));
|
||||
SFCommon.create(waitFor(function (c) { APP.common = common = c; }));
|
||||
}).nThen(function (waitFor) {
|
||||
APP.$container = $('#cp-sidebarlayout-container');
|
||||
APP.$toolbar = $('#cp-toolbar');
|
||||
APP.$leftside = $('<div>', {id: 'cp-sidebarlayout-leftside'}).appendTo(APP.$container);
|
||||
APP.$rightside = $('<div>', {id: 'cp-sidebarlayout-rightside'}).appendTo(APP.$container);
|
||||
sframeChan = common.getSframeChannel();
|
||||
sframeChan.onReady(waitFor());
|
||||
}).nThen(function (/*waitFor*/) {
|
||||
Cryptpad.onError(function (info) {
|
||||
if (info && info.type === "store") {
|
||||
onConnectError();
|
||||
}
|
||||
});
|
||||
|
||||
metadataMgr = common.getMetadataMgr();
|
||||
privateData = metadataMgr.getPrivateData();
|
||||
|
||||
// Toolbar
|
||||
var displayed = ['useradmin', 'newpad', 'limit', 'pageTitle'];
|
||||
var configTb = {
|
||||
displayed: displayed,
|
||||
common: Cryptpad,
|
||||
sfCommon: common,
|
||||
$container: APP.$toolbar,
|
||||
pageTitle: Messages.settings_title,
|
||||
metadataMgr: common.getMetadataMgr(),
|
||||
};
|
||||
APP.toolbar = Toolbar.create(configTb);
|
||||
APP.toolbar.$rightside.hide();
|
||||
|
||||
// Content
|
||||
var $rightside = APP.$rightside;
|
||||
$rightside.append(createInfoBlock());
|
||||
$rightside.append(createDisplayNameInput());
|
||||
$rightside.append(createLanguageSelector());
|
||||
$rightside.append(createIndentUnitSelector());
|
||||
$rightside.append(createIndentTypeSelector());
|
||||
|
||||
if (common.isLoggedIn()) {
|
||||
$rightside.append(createLogoutEverywhere());
|
||||
}
|
||||
$rightside.append(createResetTips());
|
||||
$rightside.append(createThumbnails());
|
||||
$rightside.append(createBackupDrive());
|
||||
$rightside.append(createImportLocalPads());
|
||||
$rightside.append(createResetDrive());
|
||||
$rightside.append(createUserFeedbackToggle());
|
||||
|
||||
// TODO RPC
|
||||
//obj.proxy.on('change', [], refresh);
|
||||
//obj.proxy.on('remove', [], refresh);
|
||||
//Cryptpad.onDisplayNameChanged(refresh);
|
||||
|
||||
createLeftside();
|
||||
createUsageButton();
|
||||
|
||||
Cryptpad.removeLoadingScreen();
|
||||
});
|
||||
});
|
|
@ -1,562 +1,42 @@
|
|||
// Load #1, load as little as possible because we are in a race to get the loading screen up.
|
||||
define([
|
||||
'/bower_components/nthen/index.js',
|
||||
'/api/config',
|
||||
'jquery',
|
||||
'/common/cryptpad-common.js',
|
||||
'/common/cryptget.js',
|
||||
'/common/mergeDrive.js',
|
||||
'/common/toolbar2.js',
|
||||
'/bower_components/file-saver/FileSaver.min.js',
|
||||
'/common/requireconfig.js',
|
||||
'/common/sframe-common-outer.js'
|
||||
], function (nThen, ApiConfig, $, RequireConfig, SFCommonO) {
|
||||
var requireConfig = RequireConfig();
|
||||
|
||||
'less!/customize/src/less/cryptpad.less',
|
||||
'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
|
||||
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
|
||||
'less!/customize/src/less/toolbar.less',
|
||||
'less!/settings/main.less',
|
||||
], function ($, Cryptpad, Crypt, Merge, Toolbar) {
|
||||
var saveAs = window.saveAs;
|
||||
|
||||
var USERNAME_KEY = 'cryptpad.username';
|
||||
|
||||
var APP = window.APP = {
|
||||
Cryptpad: Cryptpad,
|
||||
_onRefresh: []
|
||||
};
|
||||
|
||||
var Messages = Cryptpad.Messages;
|
||||
|
||||
// Manage changes in the realtime object made from another page
|
||||
var onRefresh = function (h) {
|
||||
if (typeof(h) !== "function") { return; }
|
||||
if (APP._onRefresh.indexOf(h) !== -1) { return; }
|
||||
APP._onRefresh.push(h);
|
||||
};
|
||||
var refresh = APP.refresh = function () {
|
||||
APP._onRefresh.forEach(function (h) {
|
||||
h();
|
||||
});
|
||||
};
|
||||
|
||||
var categories = {
|
||||
'account': [
|
||||
'infoBlock',
|
||||
'displayName',
|
||||
'languageSelector',
|
||||
'logoutEverywhere',
|
||||
'resetTips',
|
||||
'thumbnails',
|
||||
'userFeedback'
|
||||
],
|
||||
'drive': [
|
||||
'backupDrive',
|
||||
'importLocalPads',
|
||||
'resetDrive'
|
||||
],
|
||||
'code': [
|
||||
'indentUnit',
|
||||
'indentType'
|
||||
]
|
||||
};
|
||||
|
||||
var createInfoBlock = function (store) {
|
||||
var obj = store.proxy;
|
||||
var $div = $('<div>', {'class': 'infoBlock'});
|
||||
|
||||
var $account = $('<div>', {'class': 'element'}).appendTo($div);
|
||||
var accountName = obj.login_name || localStorage[Cryptpad.userNameKey];
|
||||
var $label = $('<span>', {'class': 'label'}).text(Messages.user_accountName);
|
||||
var $name = $('<span>').text(accountName || '');
|
||||
if (!accountName) {
|
||||
$label.text('');
|
||||
$name.text(Messages.settings_anonymous);
|
||||
}
|
||||
$account.append($label).append($name);
|
||||
|
||||
var publicKey = obj.edPublic;
|
||||
if (publicKey) {
|
||||
var $key = $('<div>', {'class': 'element'}).appendTo($div);
|
||||
var userHref = Cryptpad.getUserHrefFromKeys(accountName, publicKey);
|
||||
var $pubLabel = $('<span>', {'class': 'label'})
|
||||
.text(Messages.settings_publicSigningKey);
|
||||
$key.append($pubLabel).append(Cryptpad.dialog.selectable(userHref));
|
||||
}
|
||||
|
||||
return $div;
|
||||
};
|
||||
|
||||
// Create the block containing the display name field
|
||||
var createDisplayNameInput = function (store) {
|
||||
var obj = store.proxy;
|
||||
var $div = $('<div>', {'class': 'displayName element'});
|
||||
$('<label>', {'for' : 'displayName'}).text(Messages.user_displayName).appendTo($div);
|
||||
var $inputBlock = $('<div>', {'class': 'inputBlock'}).appendTo($div);
|
||||
var $input = $('<input>', {
|
||||
'type': 'text',
|
||||
'id': 'displayName',
|
||||
'placeholder': Messages.anonymous}).appendTo($inputBlock);
|
||||
var $save = $('<button>', {'class': 'btn btn-primary'}).text(Messages.settings_save).appendTo($inputBlock);
|
||||
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved}).hide().appendTo($div);
|
||||
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'}).hide().appendTo($div);
|
||||
|
||||
var displayName = obj[USERNAME_KEY] || '';
|
||||
$input.val(displayName);
|
||||
|
||||
// When the display name is changed (enter or button clicked)
|
||||
var todo = function () {
|
||||
displayName = $input.val();
|
||||
if (displayName === obj[USERNAME_KEY]) { return; }
|
||||
obj[USERNAME_KEY] = displayName;
|
||||
Cryptpad.changeDisplayName(displayName);
|
||||
$spinner.show();
|
||||
Cryptpad.whenRealtimeSyncs(store.info.realtime, function () {
|
||||
$spinner.hide();
|
||||
$ok.show();
|
||||
});
|
||||
// Loaded in load #2
|
||||
nThen(function (waitFor) {
|
||||
$(waitFor());
|
||||
}).nThen(function (waitFor) {
|
||||
var req = {
|
||||
cfg: requireConfig,
|
||||
req: [ '/common/loading.js' ],
|
||||
pfx: window.location.origin
|
||||
};
|
||||
$input.on('keyup', function (e) {
|
||||
if ($input.val() !== displayName) { $ok.hide(); }
|
||||
if (e.which === 13) { todo(); }
|
||||
});
|
||||
$save.click(todo);
|
||||
window.rc = requireConfig;
|
||||
window.apiconf = ApiConfig;
|
||||
$('#sbox-iframe').attr('src',
|
||||
ApiConfig.httpSafeOrigin + '/settings/inner.html?' + requireConfig.urlArgs +
|
||||
'#' + encodeURIComponent(JSON.stringify(req)));
|
||||
|
||||
// On remote change
|
||||
var onChange = function () {
|
||||
if (obj[USERNAME_KEY] !== $input.val()) {
|
||||
$input.val(obj[USERNAME_KEY]);
|
||||
$input.focusout();
|
||||
}
|
||||
// This is a cheap trick to avoid loading sframe-channel in parallel with the
|
||||
// loading screen setup.
|
||||
var done = waitFor();
|
||||
var onMsg = function (msg) {
|
||||
var data = JSON.parse(msg.data);
|
||||
if (data.q !== 'READY') { return; }
|
||||
window.removeEventListener('message', onMsg);
|
||||
var _done = done;
|
||||
done = function () { };
|
||||
_done();
|
||||
};
|
||||
onRefresh(onChange);
|
||||
|
||||
return $div;
|
||||
};
|
||||
var createIndentUnitSelector = function (obj) {
|
||||
var proxy = obj.proxy;
|
||||
proxy.settings = proxy.settings || {};
|
||||
|
||||
var $div = $('<div>', {
|
||||
'class': 'indentUnit element'
|
||||
});
|
||||
$('<label>').text(Messages.settings_codeIndentation).appendTo($div);
|
||||
|
||||
var $inputBlock = $('<div>', {
|
||||
'class': 'inputBlock',
|
||||
}).appendTo($div);
|
||||
|
||||
var $input = $('<input>', {
|
||||
'min': 1,
|
||||
'max': 8,
|
||||
type: 'number',
|
||||
}).on('change', function () {
|
||||
var val = parseInt($input.val());
|
||||
if (typeof(val) !== 'number') { return; }
|
||||
Cryptpad.setAttribute(['codemirror', 'indentUnit'], val);
|
||||
}).appendTo($inputBlock);
|
||||
|
||||
proxy.on('change', [ 'settings', 'codemirror', 'indentUnit', ], function (o, n) {
|
||||
$input.val(n);
|
||||
});
|
||||
|
||||
Cryptpad.getAttribute(['codemirror', 'indentUnit'], function (e, val) {
|
||||
if (e) { return void console.error(e); }
|
||||
if (typeof(val) !== 'number') {
|
||||
$input.val(2);
|
||||
} else {
|
||||
$input.val(val);
|
||||
}
|
||||
});
|
||||
return $div;
|
||||
};
|
||||
|
||||
var createIndentTypeSelector = function (obj) {
|
||||
var proxy = obj.proxy;
|
||||
proxy.settings = proxy.settings || {};
|
||||
|
||||
var key = 'indentWithTabs';
|
||||
|
||||
var $div = $('<div>', {
|
||||
'class': 'indentType element'
|
||||
});
|
||||
$('<label>').text(Messages.settings_codeUseTabs).appendTo($div);
|
||||
|
||||
var $inputBlock = $('<div>', {
|
||||
'class': 'inputBlock',
|
||||
}).css('flex-flow', 'column')
|
||||
.appendTo($div);
|
||||
|
||||
var $input = $('<input>', {
|
||||
type: 'checkbox',
|
||||
}).on('change', function () {
|
||||
var val = $input.is(':checked');
|
||||
if (typeof(val) !== 'boolean') { return; }
|
||||
Cryptpad.setAttribute(['codemirror', key], val);
|
||||
}).appendTo($inputBlock);
|
||||
|
||||
$input[0].checked = !!proxy.settings[key];
|
||||
proxy.on('change', ['settings', 'codemirror', key], function (o, n) {
|
||||
$input[0].checked = !!n;
|
||||
});
|
||||
|
||||
Cryptpad.getAttribute(['codemirror', key], function (e, val) {
|
||||
if (e) { return void console.error(e); }
|
||||
$input[0].checked = !!val;
|
||||
});
|
||||
return $div;
|
||||
};
|
||||
|
||||
var createResetTips = function () {
|
||||
var $div = $('<div>', {'class': 'resetTips element'});
|
||||
$('<label>', {'for' : 'resetTips'}).text(Messages.settings_resetTips).appendTo($div);
|
||||
$('<span>', {'class': 'description'})
|
||||
.text(Messages.settings_resetTipsButton).appendTo($div);
|
||||
var $button = $('<button>', {'id': 'resetTips', 'class': 'btn btn-primary'})
|
||||
.text(Messages.settings_resetTipsAction).appendTo($div);
|
||||
|
||||
$button.click(function () {
|
||||
Object.keys(localStorage).forEach(function (k) {
|
||||
if(k.slice(0, 9) === "hide-info") {
|
||||
delete localStorage[k];
|
||||
}
|
||||
});
|
||||
Cryptpad.alert(Messages.settings_resetTipsDone);
|
||||
});
|
||||
|
||||
return $div;
|
||||
};
|
||||
|
||||
var createThumbnails = function (obj) {
|
||||
var $div = $('<div>', {'class': 'thumbnails element'});
|
||||
$('<label>').text(Messages.settings_thumbnails).appendTo($div);
|
||||
|
||||
// Disable
|
||||
$('<span>', {'class': 'description'})
|
||||
.text(Messages.settings_disableThumbnailsDescription).appendTo($div);
|
||||
var $label = $('<label>', { 'for': 'disableThumbnails', 'class': 'noTitle' })
|
||||
.text(Messages.settings_disableThumbnailsAction);
|
||||
|
||||
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved});
|
||||
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'});
|
||||
|
||||
var $checkbox = $('<input>', {
|
||||
'type': 'checkbox',
|
||||
id: 'disableThumbnails'
|
||||
}).on('change', function () {
|
||||
$spinner.show();
|
||||
$ok.hide();
|
||||
if (!obj.proxy.settings) { obj.proxy.settings = {}; }
|
||||
if (!obj.proxy.settings.general) { obj.proxy.settings.general = {}; }
|
||||
obj.proxy.settings.general.disableThumbnails = $checkbox.is(':checked') || false;
|
||||
Cryptpad.whenRealtimeSyncs(obj.info.realtime, function () {
|
||||
$spinner.hide();
|
||||
$ok.show();
|
||||
});
|
||||
});
|
||||
|
||||
$checkbox.appendTo($div);
|
||||
$label.appendTo($div);
|
||||
|
||||
$ok.hide().appendTo($div);
|
||||
$spinner.hide().appendTo($div);
|
||||
|
||||
if (obj.proxy.settings && obj.proxy.settings.general
|
||||
&& obj.proxy.settings.general.disableThumbnails) {
|
||||
$checkbox[0].checked = true;
|
||||
}
|
||||
|
||||
// Reset
|
||||
/*$('<span>', {'class': 'description'})
|
||||
.text(Messages.settings_resetThumbnailsDescription).appendTo($div);
|
||||
var $button = $('<button>', {'id': 'resetThumbnails', 'class': 'btn btn-primary'})
|
||||
.text(Messages.settings_resetThumbnailsAction).appendTo($div);
|
||||
|
||||
$button.click(function () {
|
||||
// TODO we need to have access to the sandbox localforage first...
|
||||
Cryptpad.alert(Messages.settings_resetThumbnailsDone);
|
||||
});*/
|
||||
|
||||
return $div;
|
||||
};
|
||||
|
||||
var createBackupDrive = function (store) {
|
||||
var obj = store.proxy;
|
||||
var $div = $('<div>', {'class': 'backupDrive element'});
|
||||
|
||||
var exportFile = function () {
|
||||
var sjson = JSON.stringify(obj);
|
||||
var name = obj.login_name || obj[USERNAME_KEY] || Messages.anonymous;
|
||||
var suggestion = name + '-' + new Date().toDateString();
|
||||
Cryptpad.prompt(Cryptpad.Messages.exportPrompt,
|
||||
Cryptpad.fixFileName(suggestion) + '.json', function (filename) {
|
||||
if (!(typeof(filename) === 'string' && filename)) { return; }
|
||||
var blob = new Blob([sjson], {type: "application/json;charset=utf-8"});
|
||||
saveAs(blob, filename);
|
||||
});
|
||||
};
|
||||
var importFile = function (content) {
|
||||
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'}).appendTo($div);
|
||||
Crypt.put(Cryptpad.getUserHash() || localStorage[Cryptpad.fileHashKey], content, function (e) {
|
||||
if (e) { console.error(e); }
|
||||
$spinner.remove();
|
||||
});
|
||||
};
|
||||
|
||||
$('<label>', {'for' : 'exportDrive'}).text(Messages.settings_backupCategory).appendTo($div);
|
||||
$('<span>', {'class': 'description'})
|
||||
.text(Messages.settings_backupTitle).appendTo($div);
|
||||
/* add an export button */
|
||||
var $export = Cryptpad.createButton('export', true, {}, exportFile);
|
||||
$export.attr('class', 'btn btn-success').text(Messages.settings_backup);
|
||||
$div.append($export);
|
||||
|
||||
/* add an import button */
|
||||
var $import = Cryptpad.createButton('import', true, {}, importFile);
|
||||
$import.attr('class', 'btn btn-success').text(Messages.settings_restore);
|
||||
$div.append($import);
|
||||
|
||||
return $div;
|
||||
};
|
||||
|
||||
var createResetDrive = function (obj) {
|
||||
var $div = $('<div>', {'class': 'resetDrive element'});
|
||||
$('<label>', {'for' : 'resetDrive'}).text(Messages.settings_resetNewTitle).appendTo($div);
|
||||
$('<span>', {'class': 'description'})
|
||||
.text(Messages.settings_reset).appendTo($div);
|
||||
var $button = $('<button>', {'id': 'resetDrive', 'class': 'btn btn-danger'})
|
||||
.text(Messages.settings_resetButton).appendTo($div);
|
||||
|
||||
$button.click(function () {
|
||||
Cryptpad.prompt(Messages.settings_resetPrompt, "", function (val) {
|
||||
if (val !== "I love CryptPad") {
|
||||
Cryptpad.alert(Messages.settings_resetError);
|
||||
return;
|
||||
}
|
||||
obj.proxy.drive = Cryptpad.getStore().getEmptyObject();
|
||||
Cryptpad.alert(Messages.settings_resetDone);
|
||||
}, undefined, true);
|
||||
});
|
||||
|
||||
return $div;
|
||||
};
|
||||
|
||||
var createUserFeedbackToggle = function (obj) {
|
||||
var $div = $('<div>', { 'class': 'userFeedback element'});
|
||||
|
||||
$('<span>', {'class': 'label'}).text(Messages.settings_userFeedbackTitle).appendTo($div);
|
||||
|
||||
var $label = $('<label>', { 'for': 'userFeedback', 'class': 'noTitle' })
|
||||
.text(Messages.settings_userFeedback);
|
||||
|
||||
$('<span>', {'class': 'description'})
|
||||
.append(Messages.settings_userFeedbackHint1)
|
||||
.append(Messages.settings_userFeedbackHint2).appendTo($div);
|
||||
|
||||
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved});
|
||||
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'});
|
||||
|
||||
var $checkbox = $('<input>', {
|
||||
'type': 'checkbox',
|
||||
}).on('change', function () {
|
||||
$spinner.show();
|
||||
$ok.hide();
|
||||
obj.proxy.allowUserFeedback = $checkbox.is(':checked') || false;
|
||||
Cryptpad.whenRealtimeSyncs(obj.info.realtime, function () {
|
||||
$spinner.hide();
|
||||
$ok.show();
|
||||
});
|
||||
});
|
||||
|
||||
$checkbox.appendTo($div);
|
||||
$label.appendTo($div);
|
||||
|
||||
$ok.hide().appendTo($div);
|
||||
$spinner.hide().appendTo($div);
|
||||
|
||||
if (obj.proxy.allowUserFeedback) {
|
||||
$checkbox[0].checked = true;
|
||||
}
|
||||
return $div;
|
||||
};
|
||||
|
||||
var createUsageButton = function () {
|
||||
Cryptpad.createUsageBar(function (err, $bar) {
|
||||
if (err) { return void console.error(err); }
|
||||
APP.$usage.html('').append($bar);
|
||||
}, true);
|
||||
};
|
||||
|
||||
var createLogoutEverywhere = function (obj) {
|
||||
var proxy = obj.proxy;
|
||||
var $div = $('<div>', { 'class': 'logoutEverywhere element'});
|
||||
$('<label>', { 'for': 'logoutEverywhere'})
|
||||
.text(Messages.settings_logoutEverywhereTitle).appendTo($div);
|
||||
$('<span>', {'class': 'description'})
|
||||
.text(Messages.settings_logoutEverywhere).appendTo($div);
|
||||
var $button = $('<button>', { id: 'logoutEverywhere', 'class': 'btn btn-primary' })
|
||||
.text(Messages.settings_logoutEverywhereButton)
|
||||
.appendTo($div);
|
||||
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved}).hide().appendTo($div);
|
||||
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'}).hide().appendTo($div);
|
||||
|
||||
$button.click(function () {
|
||||
var realtime = obj.info.realtime;
|
||||
console.log(realtime);
|
||||
|
||||
Cryptpad.confirm(Messages.settings_logoutEverywhereConfirm, function (yes) {
|
||||
if (!yes) { return; }
|
||||
$spinner.show();
|
||||
$ok.hide();
|
||||
|
||||
var token = Math.floor(Math.random()*Number.MAX_SAFE_INTEGER);
|
||||
localStorage.setItem('loginToken', token);
|
||||
proxy.loginToken = token;
|
||||
|
||||
Cryptpad.whenRealtimeSyncs(realtime, function () {
|
||||
$spinner.hide();
|
||||
$ok.show();
|
||||
window.setTimeout(function () {
|
||||
$ok.fadeOut(1500);
|
||||
}, 2500);
|
||||
});
|
||||
});
|
||||
});
|
||||
return $div;
|
||||
};
|
||||
|
||||
var createImportLocalPads = function (obj) {
|
||||
if (!Cryptpad.isLoggedIn()) { return; }
|
||||
var $div = $('<div>', {'class': 'importLocalPads element'});
|
||||
$('<label>', {'for' : 'importLocalPads'}).text(Messages.settings_import).appendTo($div);
|
||||
$('<span>', {'class': 'description'})
|
||||
.text(Messages.settings_importTitle).appendTo($div);
|
||||
var $button = $('<button>', {'id': 'importLocalPads', 'class': 'btn btn-primary'})
|
||||
.text(Messages.settings_import).appendTo($div);
|
||||
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved}).hide().appendTo($div);
|
||||
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'}).hide().appendTo($div);
|
||||
|
||||
$button.click(function () {
|
||||
Cryptpad.confirm(Messages.settings_importConfirm, function (yes) {
|
||||
if (!yes) { return; }
|
||||
$spinner.show();
|
||||
$ok.hide();
|
||||
Merge.anonDriveIntoUser(obj, localStorage.FS_hash, function () {
|
||||
$spinner.hide();
|
||||
$ok.show();
|
||||
Cryptpad.alert(Messages.settings_importDone);
|
||||
});
|
||||
}, undefined, true);
|
||||
});
|
||||
|
||||
return $div;
|
||||
};
|
||||
|
||||
var createLanguageSelector = function () {
|
||||
var $div = $('<div>', {'class': 'languageSelector element'});
|
||||
$('<label>').text(Messages.language).appendTo($div);
|
||||
var $b = Cryptpad.createLanguageSelector().appendTo($div);
|
||||
$b.find('button').addClass('btn btn-secondary');
|
||||
return $div;
|
||||
};
|
||||
|
||||
|
||||
var hideCategories = function () {
|
||||
APP.$rightside.find('> div').hide();
|
||||
};
|
||||
var showCategories = function (cat) {
|
||||
hideCategories();
|
||||
cat.forEach(function (c) {
|
||||
APP.$rightside.find('.'+c).show();
|
||||
});
|
||||
};
|
||||
|
||||
var createLeftside = function () {
|
||||
var $categories = $('<div>', {'class': 'categories'}).appendTo(APP.$leftside);
|
||||
APP.$usage = $('<div>', {'class': 'usage'}).appendTo(APP.$leftside);
|
||||
var active = 'account';
|
||||
Object.keys(categories).forEach(function (key) {
|
||||
var $category = $('<div>', {'class': 'category'}).appendTo($categories);
|
||||
if (key === 'account') { $category.append($('<span>', {'class': 'fa fa-user-o'})); }
|
||||
if (key === 'drive') { $category.append($('<span>', {'class': 'fa fa-hdd-o'})); }
|
||||
if (key === 'code') { $category.append($('<span>', {'class': 'fa fa-file-code-o' })); }
|
||||
|
||||
if (key === active) {
|
||||
$category.addClass('active');
|
||||
}
|
||||
|
||||
$category.click(function () {
|
||||
active = key;
|
||||
$categories.find('.active').removeClass('active');
|
||||
$category.addClass('active');
|
||||
showCategories(categories[key]);
|
||||
});
|
||||
|
||||
$category.append(Messages['settings_cat_'+key]);
|
||||
});
|
||||
showCategories(categories[active]);
|
||||
};
|
||||
|
||||
var createToolbar = function () {
|
||||
var displayed = ['useradmin', 'newpad', 'limit', 'upgrade', 'pageTitle'];
|
||||
var configTb = {
|
||||
displayed: displayed,
|
||||
ifrw: window,
|
||||
common: Cryptpad,
|
||||
$container: APP.$toolbar,
|
||||
pageTitle: Messages.settings_title
|
||||
};
|
||||
var toolbar = APP.toolbar = Toolbar.create(configTb);
|
||||
toolbar.$rightside.html(''); // Remove the drawer if we don't use it to hide the toolbar
|
||||
};
|
||||
|
||||
var andThen = function (obj) {
|
||||
APP.$leftside = $('<div>', {id: 'leftSide'}).appendTo(APP.$container);
|
||||
var $rightside = APP.$rightside = $('<div>', {id: 'rightSide'}).appendTo(APP.$container);
|
||||
|
||||
createToolbar();
|
||||
|
||||
//$rightside.append(createTitle());
|
||||
$rightside.append(createInfoBlock(obj));
|
||||
$rightside.append(createDisplayNameInput(obj));
|
||||
$rightside.append(createLanguageSelector());
|
||||
$rightside.append(createIndentUnitSelector(obj));
|
||||
$rightside.append(createIndentTypeSelector(obj));
|
||||
|
||||
if (Cryptpad.isLoggedIn()) {
|
||||
$rightside.append(createLogoutEverywhere(obj));
|
||||
}
|
||||
$rightside.append(createResetTips());
|
||||
$rightside.append(createThumbnails(obj));
|
||||
$rightside.append(createBackupDrive(obj));
|
||||
$rightside.append(createImportLocalPads(obj));
|
||||
$rightside.append(createResetDrive(obj));
|
||||
$rightside.append(createUserFeedbackToggle(obj));
|
||||
|
||||
obj.proxy.on('change', [], refresh);
|
||||
obj.proxy.on('remove', [], refresh);
|
||||
Cryptpad.onDisplayNameChanged(refresh);
|
||||
|
||||
createLeftside();
|
||||
createUsageButton();
|
||||
|
||||
Cryptpad.removeLoadingScreen();
|
||||
};
|
||||
|
||||
$(function () {
|
||||
$(window).click(function () {
|
||||
$('.cp-dropdown-content').hide();
|
||||
});
|
||||
|
||||
APP.$container = $('#container');
|
||||
APP.$toolbar = $('#toolbar');
|
||||
|
||||
Cryptpad.ready(function () {
|
||||
//if (!Cryptpad.getUserHash()) { return redirectToMain(); }
|
||||
|
||||
var storeObj = Cryptpad.getStore().getProxy && Cryptpad.getStore().getProxy().proxy
|
||||
? Cryptpad.getStore().getProxy() : undefined;
|
||||
|
||||
andThen(storeObj);
|
||||
Cryptpad.reportAppUsage();
|
||||
});
|
||||
});
|
||||
|
||||
window.addEventListener('message', onMsg);
|
||||
}).nThen(function (/*waitFor*/) {
|
||||
/* TODO
|
||||
window.addEventListener('storage', function (e) {
|
||||
if (e.key !== Cryptpad.userHashKey) { return; }
|
||||
var o = e.oldValue;
|
||||
|
@ -566,4 +46,46 @@ define([
|
|||
//redirectToMain();
|
||||
}
|
||||
});
|
||||
*/
|
||||
var addRpc = function (sframeChan, Cryptpad) {
|
||||
sframeChan.on('Q_THUMBNAIL_CLEAR', function (d, cb) {
|
||||
Cryptpad.clearThumbnail(function (err, data) {
|
||||
cb({err:err, data:data});
|
||||
});
|
||||
});
|
||||
sframeChan.on('Q_SETTINGS_DRIVE_GET', function (d, cb) {
|
||||
cb(Cryptpad.getProxy());
|
||||
});
|
||||
sframeChan.on('Q_SETTINGS_DRIVE_SET', function (data, cb) {
|
||||
var sjson = JSON.stringify(data);
|
||||
var k = Cryptpad.getUserHash() || localStorage[Cryptpad.fileHashKey];
|
||||
require(['/common/cryptget.js'], function (Crypt) {
|
||||
Crypt.put(k, sjson, function (err, data) {
|
||||
cb(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
sframeChan.on('Q_SETTINGS_DRIVE_RESET', function (data, cb) {
|
||||
var proxy = Cryptpad.getProxy();
|
||||
var realtime = Cryptpad.getRealtime();
|
||||
proxy.drive = Cryptpad.getStore().getEmptyObject();
|
||||
Cryptpad.whenRealtimeSyncs(realtime, cb);
|
||||
});
|
||||
sframeChan.on('Q_SETTINGS_LOGOUT', function (data, cb) {
|
||||
var proxy = Cryptpad.getProxy();
|
||||
var realtime = Cryptpad.getRealtime();
|
||||
var token = Math.floor(Math.random()*Number.MAX_SAFE_INTEGER);
|
||||
localStorage.setItem('loginToken', token);
|
||||
proxy.loginToken = token;
|
||||
Cryptpad.whenRealtimeSyncs(realtime, cb);
|
||||
});
|
||||
sframeChan.on('Q_SETTINGS_IMPORT_LOCAL', function (data, cb) {
|
||||
Merge.anonDriveIntoUser(Cryptpad.getStore().getProxy(), localStorage.FS_hash, cb);
|
||||
});
|
||||
};
|
||||
SFCommonO.start({
|
||||
noRealtime: true,
|
||||
addRpc: addRpc
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,528 @@
|
|||
define([
|
||||
'jquery',
|
||||
'/common/cryptpad-common.js',
|
||||
'/common/hyperscript.js',
|
||||
'/bower_components/marked/marked.min.js',
|
||||
'/common/media-tag.js',
|
||||
], function ($, Cryptpad, h, Marked, MediaTag) {
|
||||
'use strict';
|
||||
|
||||
var UI = {};
|
||||
var Messages = Cryptpad.Messages;
|
||||
|
||||
var m = function (md) {
|
||||
var d = h('div.cp-app-contacts-content');
|
||||
try {
|
||||
d.innerHTML = Marked(md || '');
|
||||
var $d = $(d);
|
||||
// remove potentially malicious elements
|
||||
$d.find('script, iframe, object, applet, video, audio').remove();
|
||||
|
||||
// activate media-tags
|
||||
$d.find('media-tag').each(function (i, e) { MediaTag(e); });
|
||||
} catch (e) {
|
||||
console.error(md);
|
||||
console.error(e);
|
||||
}
|
||||
return d;
|
||||
};
|
||||
|
||||
var dataQuery = function (curvePublic) {
|
||||
return '[data-key="' + curvePublic + '"]';
|
||||
};
|
||||
|
||||
var initChannel = function (state, curvePublic, info) {
|
||||
console.log('initializing channel for [%s]', curvePublic);
|
||||
state.channels[curvePublic] = {
|
||||
messages: [],
|
||||
HEAD: info.lastKnownHash,
|
||||
TAIL: null,
|
||||
};
|
||||
};
|
||||
|
||||
UI.create = function (messenger, $userlist, $messages, common) {
|
||||
var origin = common.getMetadataMgr().getPrivateData().origin;
|
||||
|
||||
var state = window.state = {
|
||||
active: '',
|
||||
};
|
||||
|
||||
state.channels = {};
|
||||
var displayNames = state.displayNames = {};
|
||||
|
||||
var avatars = state.avatars = {};
|
||||
var setActive = function (curvePublic) {
|
||||
state.active = curvePublic;
|
||||
};
|
||||
var isActive = function (curvePublic) {
|
||||
return curvePublic === state.active;
|
||||
};
|
||||
|
||||
var find = {};
|
||||
find.inList = function (curvePublic) {
|
||||
return $userlist.find(dataQuery(curvePublic));
|
||||
};
|
||||
|
||||
var notify = function (curvePublic) {
|
||||
find.inList(curvePublic).addClass('cp-app-contacts-notify');
|
||||
};
|
||||
var unnotify = function (curvePublic) {
|
||||
find.inList(curvePublic).removeClass('cp-app-contacts-notify');
|
||||
};
|
||||
|
||||
var markup = {};
|
||||
markup.message = function (msg) {
|
||||
var curvePublic = msg.author;
|
||||
var name = displayNames[msg.author];
|
||||
return h('div.cp-app-contacts-message', {
|
||||
title: msg.time? new Date(msg.time).toLocaleString(): '?',
|
||||
'data-key': curvePublic,
|
||||
}, [
|
||||
name? h('div.cp-app-contacts-sender', name): undefined,
|
||||
m(msg.text),
|
||||
]);
|
||||
};
|
||||
|
||||
var getChat = function (curvePublic) {
|
||||
return $messages.find(dataQuery(curvePublic));
|
||||
};
|
||||
|
||||
var normalizeLabels = function ($messagebox) {
|
||||
$messagebox.find('div.cp-app-contacts-message').toArray().reduce(function (a, b) {
|
||||
var $b = $(b);
|
||||
if ($(a).data('key') === $b.data('key')) {
|
||||
$b.find('.cp-app-contacts-sender').hide();
|
||||
return a;
|
||||
}
|
||||
return b;
|
||||
}, []);
|
||||
};
|
||||
|
||||
markup.chatbox = function (curvePublic, data) {
|
||||
var moreHistory = h('span.cp-app-contacts-more-history.fa.fa-history', {
|
||||
title: Messages.contacts_fetchHistory,
|
||||
});
|
||||
var displayName = data.displayName;
|
||||
|
||||
var fetching = false;
|
||||
var $moreHistory = $(moreHistory).click(function () {
|
||||
if (fetching) { return; }
|
||||
|
||||
// get oldest known message...
|
||||
var channel = state.channels[curvePublic];
|
||||
|
||||
if (channel.exhausted) {
|
||||
return void $moreHistory.addClass('cp-app-contacts-faded');
|
||||
}
|
||||
|
||||
console.log('getting history');
|
||||
var sig = channel.TAIL || channel.HEAD;
|
||||
|
||||
fetching = true;
|
||||
var $messagebox = $(getChat(curvePublic)).find('.cp-app-contacts-messages');
|
||||
messenger.getMoreHistory(curvePublic, sig, 10, function (e, history) {
|
||||
fetching = false;
|
||||
if (e) { return void console.error(e); }
|
||||
|
||||
if (history.length === 0) {
|
||||
channel.exhausted = true;
|
||||
return;
|
||||
}
|
||||
|
||||
history.forEach(function (msg) {
|
||||
if (channel.exhausted) { return; }
|
||||
if (msg.sig) {
|
||||
if (msg.sig === channel.TAIL) {
|
||||
console.error('No more messages to fetch');
|
||||
channel.exhausted = true;
|
||||
console.log(channel);
|
||||
return void $moreHistory.addClass('cp-app-contacts-faded');
|
||||
} else {
|
||||
channel.TAIL = msg.sig;
|
||||
}
|
||||
} else {
|
||||
return void console.error('expected signature');
|
||||
}
|
||||
if (msg.type !== 'MSG') { return; }
|
||||
|
||||
// FIXME Schlameil the painter (performance does not scale well)
|
||||
if (channel.messages.some(function (old) {
|
||||
return msg.sig === old.sig;
|
||||
})) { return; }
|
||||
|
||||
channel.messages.unshift(msg);
|
||||
var el_message = markup.message(msg);
|
||||
$messagebox.prepend(el_message);
|
||||
});
|
||||
normalizeLabels($messagebox);
|
||||
});
|
||||
});
|
||||
|
||||
var removeHistory = h('span.cp-app-contacts-remove-history.fa.fa-eraser', {
|
||||
title: Messages.contacts_removeHistoryTitle
|
||||
});
|
||||
|
||||
$(removeHistory).click(function () {
|
||||
Cryptpad.confirm(Messages.contacts_confirmRemoveHistory, function (yes) {
|
||||
if (!yes) { return; }
|
||||
Cryptpad.clearOwnedChannel(data.channel, function (e) {
|
||||
if (e) {
|
||||
console.error(e);
|
||||
Cryptpad.alert(Messages.contacts_removeHistoryServerError);
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
var avatar = h('div.cp-avatar');
|
||||
var header = h('div.cp-app-contacts-header', [
|
||||
avatar,
|
||||
moreHistory,
|
||||
removeHistory,
|
||||
]);
|
||||
var messages = h('div.cp-app-contacts-messages');
|
||||
var input = h('textarea', {
|
||||
placeholder: Messages.contacts_typeHere
|
||||
});
|
||||
var sendButton = h('button.btn.btn-primary.fa.fa-paper-plane', {
|
||||
title: Messages.contacts_send,
|
||||
});
|
||||
|
||||
var rightCol = h('span.cp-app-contacts-right-col', [
|
||||
h('span.cp-app-contacts-name', displayName),
|
||||
]);
|
||||
|
||||
var $avatar = $(avatar);
|
||||
if (data.avatar && avatars[data.avatar]) {
|
||||
$avatar.append(avatars[data.avatar]).append(rightCol);
|
||||
} else {
|
||||
common.displayAvatar($avatar, data.avatar, data.displayName, function ($img) {
|
||||
if (data.avatar && $img) {
|
||||
avatars[data.avatar] = $img[0].outerHTML;
|
||||
}
|
||||
$(rightCol).insertAfter($avatar);
|
||||
});
|
||||
}
|
||||
|
||||
var sending = false;
|
||||
var send = function (content) {
|
||||
if (typeof(content) !== 'string' || !content.trim()) { return; }
|
||||
if (sending) { return false; }
|
||||
sending = true;
|
||||
messenger.sendMessage(curvePublic, content, function (e) {
|
||||
if (e) {
|
||||
// failed to send
|
||||
return void console.error('failed to send');
|
||||
}
|
||||
input.value = '';
|
||||
sending = false;
|
||||
console.log('sent successfully');
|
||||
var $messagebox = $(messages);
|
||||
|
||||
var height = $messagebox[0].scrollHeight;
|
||||
$messagebox.scrollTop(height);
|
||||
});
|
||||
};
|
||||
|
||||
var onKeyDown = function (e) {
|
||||
// ignore anything that isn't 'enter'
|
||||
if (e.keyCode !== 13) { return; }
|
||||
// send unless they're holding a ctrl-key or shift
|
||||
if (!e.ctrlKey && !e.shiftKey) {
|
||||
send(this.value);
|
||||
return false;
|
||||
}
|
||||
|
||||
// insert a newline if they're holding either
|
||||
var val = this.value;
|
||||
var start = this.selectionState;
|
||||
var end = this.selectionEnd;
|
||||
|
||||
if (![start,end].some(function (x) {
|
||||
return typeof(x) !== 'number';
|
||||
})) {
|
||||
this.value = val.slice(0, start) + '\n' + val.slice(end);
|
||||
this.selectionStart = this.selectionEnd = start + 1;
|
||||
} else if (document.selection && document.selection.createRange) {
|
||||
this.focus();
|
||||
var range = document.selection.createRange();
|
||||
range.text = '\r\n';
|
||||
range.collapse(false);
|
||||
range.select();
|
||||
}
|
||||
return false;
|
||||
};
|
||||
$(input).on('keydown', onKeyDown);
|
||||
$(sendButton).click(function () { send(input.value); });
|
||||
|
||||
return h('div.cp-app-contacts-chat', {
|
||||
'data-key': curvePublic,
|
||||
}, [
|
||||
header,
|
||||
messages,
|
||||
h('div.cp-app-contacts-input', [
|
||||
input,
|
||||
sendButton,
|
||||
]),
|
||||
]);
|
||||
};
|
||||
|
||||
var hideInfo = function () {
|
||||
$messages.find('.cp-app-contacts-info').hide();
|
||||
};
|
||||
|
||||
var updateStatus = function (curvePublic) {
|
||||
var $status = find.inList(curvePublic).find('.cp-app-contacts-status');
|
||||
// FIXME this stopped working :(
|
||||
messenger.getStatus(curvePublic, function (e, online) {
|
||||
// if error maybe you shouldn't display this friend...
|
||||
if (e) {
|
||||
find.inList(curvePublic).hide();
|
||||
getChat(curvePublic).hide();
|
||||
|
||||
return void console.error(curvePublic, e);
|
||||
}
|
||||
if (online) {
|
||||
return void $status
|
||||
.removeClass('cp-app-contacts-offline').addClass('cp-app-contacts-online');
|
||||
}
|
||||
$status.removeClass('cp-app-contacts-online').addClass('cp-app-contacts-offline');
|
||||
});
|
||||
};
|
||||
|
||||
var display = function (curvePublic) {
|
||||
var channel = state.channels[curvePublic];
|
||||
var lastMsg = channel.messages.slice(-1)[0];
|
||||
|
||||
if (lastMsg) {
|
||||
channel.HEAD = lastMsg.sig;
|
||||
messenger.setChannelHead(curvePublic, channel.HEAD, function (e) {
|
||||
if (e) { console.error(e); }
|
||||
});
|
||||
}
|
||||
|
||||
setActive(curvePublic);
|
||||
unnotify(curvePublic);
|
||||
var $chat = getChat(curvePublic);
|
||||
hideInfo();
|
||||
$messages.find('div.cp-app-contacts-chat[data-key]').hide();
|
||||
if ($chat.length) {
|
||||
var $chat_messages = $chat.find('div.cp-app-contacts-message');
|
||||
if (!$chat_messages.length) {
|
||||
var $more = $chat.find('.cp-app-contacts-more-history');
|
||||
$more.click();
|
||||
}
|
||||
return void $chat.show();
|
||||
}
|
||||
messenger.getFriendInfo(curvePublic, function (e, info) {
|
||||
if (e) { return void console.error(e); } // FIXME
|
||||
var chatbox = markup.chatbox(curvePublic, info);
|
||||
$messages.append(chatbox);
|
||||
});
|
||||
};
|
||||
|
||||
var removeFriend = function (curvePublic) {
|
||||
messenger.removeFriend(curvePublic, function (e /*, removed */) {
|
||||
if (e) { return void console.error(e); }
|
||||
find.inList(curvePublic).remove();
|
||||
//console.log(removed);
|
||||
});
|
||||
};
|
||||
|
||||
markup.friend = function (data) {
|
||||
var curvePublic = data.curvePublic;
|
||||
var friend = h('div.cp-app-contacts-friend.cp-avatar', {
|
||||
'data-key': curvePublic,
|
||||
});
|
||||
|
||||
var remove = h('span.cp-app-contacts-remove.fa.fa-user-times', {
|
||||
title: Messages.contacts_remove
|
||||
});
|
||||
var status = h('span.cp-app-contacts-status');
|
||||
var rightCol = h('span.cp-app-contacts-right-col', [
|
||||
h('span.cp-app-contacts-name', [data.displayName]),
|
||||
remove,
|
||||
]);
|
||||
|
||||
var $friend = $(friend)
|
||||
.click(function () {
|
||||
display(curvePublic);
|
||||
})
|
||||
.dblclick(function () {
|
||||
if (data.profile) { window.open(origin + '/profile/#' + data.profile); }
|
||||
});
|
||||
|
||||
$(remove).click(function (e) {
|
||||
e.stopPropagation();
|
||||
Cryptpad.confirm(Messages._getKey('contacts_confirmRemove', [
|
||||
Cryptpad.fixHTML(data.displayName)
|
||||
]), function (yes) {
|
||||
if (!yes) { return; }
|
||||
removeFriend(curvePublic, function (e) {
|
||||
if (e) { return void console.error(e); }
|
||||
});
|
||||
// TODO remove friend from userlist ui
|
||||
// FIXME seems to trigger EJOINED from netflux-websocket (from server);
|
||||
// (tried to join a channel in which you were already present)
|
||||
}, undefined, true);
|
||||
});
|
||||
|
||||
if (data.avatar && avatars[data.avatar]) {
|
||||
$friend.append(avatars[data.avatar]);
|
||||
$friend.append(rightCol);
|
||||
} else {
|
||||
common.displayAvatar($friend, data.avatar, data.displayName, function ($img) {
|
||||
if (data.avatar && $img) {
|
||||
avatars[data.avatar] = $img[0].outerHTML;
|
||||
}
|
||||
$friend.append(rightCol);
|
||||
});
|
||||
}
|
||||
$friend.append(status);
|
||||
return $friend;
|
||||
};
|
||||
|
||||
var isBottomedOut = function ($elem) {
|
||||
return ($elem[0].scrollHeight - $elem.scrollTop() === $elem.outerHeight());
|
||||
};
|
||||
|
||||
var initializing = true;
|
||||
messenger.on('message', function (message) {
|
||||
if (!initializing) { Cryptpad.notify(); }
|
||||
var curvePublic = message.curve;
|
||||
|
||||
var name = displayNames[curvePublic];
|
||||
var chat = getChat(curvePublic, name);
|
||||
|
||||
console.log(message);
|
||||
|
||||
var el_message = markup.message(message);
|
||||
|
||||
state.channels[curvePublic].messages.push(message);
|
||||
|
||||
var $chat = $(chat);
|
||||
|
||||
if (!$chat.length) {
|
||||
console.error("Got a message but the chat isn't open");
|
||||
}
|
||||
|
||||
var $messagebox = $chat.find('.cp-app-contacts-messages');
|
||||
var shouldScroll = isBottomedOut($messagebox);
|
||||
|
||||
$messagebox.append(el_message);
|
||||
|
||||
if (shouldScroll) {
|
||||
$messagebox.scrollTop($messagebox.outerHeight());
|
||||
}
|
||||
normalizeLabels($messagebox);
|
||||
|
||||
var channel = state.channels[curvePublic];
|
||||
if (!channel) {
|
||||
console.error('expected channel [%s] to be open', curvePublic);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isActive(curvePublic)) {
|
||||
channel.HEAD = message.sig;
|
||||
messenger.setChannelHead(curvePublic, message.sig, function (e) {
|
||||
if (e) { return void console.error(e); }
|
||||
});
|
||||
return;
|
||||
}
|
||||
var lastMsg = channel.messages.slice(-1)[0];
|
||||
if (lastMsg.sig !== channel.HEAD) {
|
||||
return void notify(curvePublic);
|
||||
}
|
||||
unnotify(curvePublic);
|
||||
});
|
||||
|
||||
messenger.on('join', function (curvePublic, channel) {
|
||||
channel = channel;
|
||||
updateStatus(curvePublic);
|
||||
});
|
||||
messenger.on('leave', function (curvePublic, channel) {
|
||||
channel = channel;
|
||||
updateStatus(curvePublic);
|
||||
});
|
||||
|
||||
// change in your friend list
|
||||
messenger.on('update', function (info, curvePublic) {
|
||||
var name = displayNames[curvePublic] = info.displayName;
|
||||
|
||||
// update label in friend list
|
||||
find.inList(curvePublic).find('.cp-app-contacts-name').text(name);
|
||||
|
||||
// update title bar and messages
|
||||
$messages.find(dataQuery(curvePublic) + ' .cp-app-contacts-header ' +
|
||||
'.cp-app-contacts-name, div.cp-app-contacts-message'+
|
||||
dataQuery(curvePublic) + ' div.cp-app-contacts-sender').text(name).text(name);
|
||||
});
|
||||
|
||||
var connectToFriend = function (curvePublic, cb) {
|
||||
messenger.getFriendInfo(curvePublic, function (e, info) {
|
||||
if (e) { return void console.error(e); }
|
||||
var name = displayNames[curvePublic] = info.displayName;
|
||||
initChannel(state, curvePublic, info);
|
||||
|
||||
var chatbox = markup.chatbox(curvePublic, info);
|
||||
$(chatbox).hide();
|
||||
$messages.append(chatbox);
|
||||
|
||||
var friend = markup.friend(info, name);
|
||||
$userlist.append(friend);
|
||||
messenger.openFriendChannel(curvePublic, function (e) {
|
||||
if (e) { return void console.error(e); }
|
||||
cb();
|
||||
updateStatus(curvePublic);
|
||||
// don't add friends that are already in your userlist
|
||||
//if (friendExistsInUserList(k)) { return; }
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
messenger.on('friend', function (curvePublic) {
|
||||
console.log('new friend: ', curvePublic);
|
||||
//console.error("TODO redraw user list");
|
||||
//console.error("TODO connect to new friend");
|
||||
// FIXME this doesn't work right now because the friend hasn't been fully added?
|
||||
connectToFriend(curvePublic, function () {
|
||||
//console.error('connected');
|
||||
});
|
||||
});
|
||||
|
||||
messenger.on('unfriend', function (curvePublic) {
|
||||
console.log('unfriend', curvePublic);
|
||||
find.inList(curvePublic).remove();
|
||||
console.error('TODO remove chatbox');
|
||||
console.error('TODO show something if that chatbox was active');
|
||||
});
|
||||
|
||||
Cryptpad.onDisplayNameChanged(function () {
|
||||
//messenger.checkNewFriends();
|
||||
messenger.updateMyData();
|
||||
});
|
||||
|
||||
// FIXME dirty hack
|
||||
messenger.getMyInfo(function (e, info) {
|
||||
displayNames[info.curvePublic] = info.displayName;
|
||||
});
|
||||
|
||||
messenger.getFriendList(function (e, keys) {
|
||||
var count = keys.length + 1;
|
||||
var ready = function () {
|
||||
count--;
|
||||
if (count === 0) {
|
||||
initializing = false;
|
||||
Cryptpad.removeLoadingScreen();
|
||||
}
|
||||
};
|
||||
ready();
|
||||
keys.forEach(function (curvePublic) {
|
||||
connectToFriend(curvePublic, ready);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return UI;
|
||||
});
|
Loading…
Reference in New Issue