24 changed files with 2065 additions and 671 deletions
Split View
Diff Options
-
86customize.dist/messages.js
-
18customize.dist/pages.js
-
10customize.dist/src/less2/include/sidebar-layout.less
-
1customize.dist/src/less2/main.less
-
81www/common/common-language.js
-
22www/common/cryptpad-common.js
-
6www/common/feedback-main.js
-
7www/common/fsStore.js
-
17www/common/migrate-user-object.js
-
40www/common/sframe-common-interface.js
-
5www/common/sframe-common-outer.js
-
1www/common/sframe-common.js
-
15www/common/sframe-protocol.js
-
4www/drive/app-drive.less
-
16www/oldsettings/index.html
-
569www/oldsettings/main.js
-
0www/oldsettings/main.less
-
2www/profile/app-profile.less
-
67www/settings/app-settings.less
-
46www/settings/index.html
-
18www/settings/inner.html
-
551www/settings/inner.js
-
626www/settings/main.js
-
528www/settings/messenger-ui.js
@ -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; |
|||
|
|||
}); |
|||
@ -1,5 +1,5 @@ |
|||
define([ |
|||
'/customize/messages.js', |
|||
], function (Messages) { |
|||
Messages._applyTranslation(); |
|||
'/common/common-language.js', |
|||
], function (Language) { |
|||
Language.applyTranslation(); |
|||
}); |
|||
@ -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();
|
|||
} |
|||
}); |
|||
}); |
|||
@ -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,569 +1,91 @@ |
|||
// 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', |
|||
|
|||
'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(); |
|||
}); |
|||
'/common/requireconfig.js', |
|||
'/common/sframe-common-outer.js' |
|||
], function (nThen, ApiConfig, $, RequireConfig, SFCommonO) { |
|||
var requireConfig = RequireConfig(); |
|||
|
|||
// 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); |
|||
|
|||
// On remote change
|
|||
var onChange = function () { |
|||
if (obj[USERNAME_KEY] !== $input.val()) { |
|||
$input.val(obj[USERNAME_KEY]); |
|||
$input.focusout(); |
|||
} |
|||
window.rc = requireConfig; |
|||
window.apiconf = ApiConfig; |
|||
$('#sbox-iframe').attr('src', |
|||
ApiConfig.httpSafeOrigin + '/settings/inner.html?' + requireConfig.urlArgs + |
|||
'#' + encodeURIComponent(JSON.stringify(req))); |
|||
|
|||
// 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; |
|||
window.addEventListener('message', onMsg); |
|||
}).nThen(function (/*waitFor*/) { |
|||
/* TODO |
|||
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();
|
|||
} |
|||
|
|||
// 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 addRpc = function (sframeChan, Cryptpad) { |
|||
sframeChan.on('Q_THUMBNAIL_CLEAR', function (d, cb) { |
|||
Cryptpad.clearThumbnail(function (err, data) { |
|||
cb({err:err, data:data}); |
|||
}); |
|||
}); |
|||
}; |
|||
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(); |
|||
sframeChan.on('Q_SETTINGS_DRIVE_GET', function (d, cb) { |
|||
cb(Cryptpad.getProxy()); |
|||
}); |
|||
}; |
|||
|
|||
$('<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(); |
|||
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); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
$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(); |
|||
|
|||
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, function () { |
|||
$spinner.hide(); |
|||
$ok.show(); |
|||
window.setTimeout(function () { |
|||
$ok.fadeOut(1500); |
|||
}, 2500); |
|||
}); |
|||
Cryptpad.whenRealtimeSyncs(realtime, cb); |
|||
}); |
|||
}); |
|||
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]); |
|||
sframeChan.on('Q_SETTINGS_IMPORT_LOCAL', function (data, cb) { |
|||
Merge.anonDriveIntoUser(Cryptpad.getStore().getProxy(), localStorage.FS_hash, cb); |
|||
}); |
|||
|
|||
$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(); |
|||
SFCommonO.start({ |
|||
noRealtime: true, |
|||
addRpc: addRpc |
|||
}); |
|||
}); |
|||
|
|||
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();
|
|||
} |
|||
}); |
|||
}); |
|||
@ -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; |
|||
}); |
|||
Write
Preview
Loading…
Cancel
Save