mirror of
https://github.com/the-djmaze/snappymail.git
synced 2024-09-20 07:35:55 +08:00
Add cmd interface
This commit is contained in:
parent
17669b7be0
commit
e6e1a19477
|
@ -115,9 +115,12 @@ class AbstractApp extends AbstractBoot
|
|||
detectDropdownVisibility();
|
||||
});
|
||||
|
||||
key('ctrl+shift+`', KeyState.All, () => {
|
||||
toggleCmd();
|
||||
});
|
||||
if (Settings.appSettingsGet('allowCmdInterface'))
|
||||
{
|
||||
key('ctrl+shift+`', KeyState.All, () => {
|
||||
toggleCmd();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
remote() {
|
||||
|
|
|
@ -4,7 +4,7 @@ import _ from '_';
|
|||
import ko from 'ko';
|
||||
import progressJs from 'progressJs';
|
||||
|
||||
import * as Links from 'Common/Links';
|
||||
import {root} from 'Common/Links';
|
||||
import {getNotification} from 'Common/Translator';
|
||||
import {StorageResultType, Notification} from 'Common/Enums';
|
||||
import {pInt, isNormal, isArray, inArray, isUnd} from 'Common/Utils';
|
||||
|
@ -225,7 +225,7 @@ class AdminApp extends AbstractApp
|
|||
if (!Settings.appSettingsGet('allowAdminPanel'))
|
||||
{
|
||||
routeOff();
|
||||
setHash(Links.root(), true);
|
||||
setHash(root(), true);
|
||||
routeOff();
|
||||
|
||||
_.defer(() => {
|
||||
|
|
118
dev/App/User.js
118
dev/App/User.js
|
@ -22,11 +22,24 @@ import {
|
|||
} from 'Common/Globals';
|
||||
|
||||
import {UNUSED_OPTION_VALUE} from 'Common/Consts';
|
||||
import * as Plugins from 'Common/Plugins';
|
||||
import * as Links from 'Common/Links';
|
||||
import {runHook} from 'Common/Plugins';
|
||||
import {momentNowUnix, reload as momentReload} from 'Common/Momentor';
|
||||
|
||||
import {
|
||||
initMessageFlagsFromCache, setFolderHash, getFolderHash, getFolderInboxName,
|
||||
getFolderFromCacheList, clearMessageFlagsFromCacheByFolder,
|
||||
storeMessageFlagsToCacheBySetAction,
|
||||
storeMessageFlagsToCacheByFolderAndUid
|
||||
} from 'Common/Cache';
|
||||
|
||||
import {
|
||||
userBackground, mailBox, root,
|
||||
openPgpWorkerJs, openPgpJs,
|
||||
socialGoogle, socialTwitter, socialFacebook
|
||||
} from 'Common/Links';
|
||||
|
||||
import * as Events from 'Common/Events';
|
||||
import * as Momentor from 'Common/Momentor';
|
||||
import * as Cache from 'Common/Cache';
|
||||
|
||||
import {getNotification, i18n} from 'Common/Translator';
|
||||
|
||||
import SocialStore from 'Stores/Social';
|
||||
|
@ -114,7 +127,7 @@ class AppUser extends AbstractApp
|
|||
_.delay(() => {
|
||||
$('#rl-bg')
|
||||
.attr('style', 'background-image: none !important;')
|
||||
.backstretch(Links.userBackground(Settings.settingsGet('UserBackgroundHash')), {
|
||||
.backstretch(userBackground(Settings.settingsGet('UserBackgroundHash')), {
|
||||
fade: bAnimationSupported ? 1000 : 0,
|
||||
centeredX: true,
|
||||
centeredY: true
|
||||
|
@ -143,9 +156,9 @@ class AppUser extends AbstractApp
|
|||
|
||||
reloadFlagsCurrentMessageListAndMessageFromCache() {
|
||||
_.each(MessageStore.messageList(), (message) => {
|
||||
Cache.initMessageFlagsFromCache(message);
|
||||
initMessageFlagsFromCache(message);
|
||||
});
|
||||
Cache.initMessageFlagsFromCache(MessageStore.message());
|
||||
initMessageFlagsFromCache(MessageStore.message());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -159,7 +172,7 @@ class AppUser extends AbstractApp
|
|||
|
||||
if (bDropCurrenFolderCache)
|
||||
{
|
||||
Cache.setFolderHash(FolderStore.currentFolderFullNameRaw(), '');
|
||||
setFolderHash(FolderStore.currentFolderFullNameRaw(), '');
|
||||
}
|
||||
|
||||
if (bDropPagePosition)
|
||||
|
@ -168,7 +181,7 @@ class AppUser extends AbstractApp
|
|||
MessageStore.messageListPageBeforeThread(1);
|
||||
iOffset = 0;
|
||||
|
||||
setHash(Links.mailBox(
|
||||
setHash(mailBox(
|
||||
FolderStore.currentFolderFullNameHash(),
|
||||
MessageStore.messageListPage(),
|
||||
MessageStore.messageListSearch(),
|
||||
|
@ -205,7 +218,7 @@ class AppUser extends AbstractApp
|
|||
}
|
||||
|
||||
recacheInboxMessageList() {
|
||||
Remote.messageList(noop, Cache.getFolderInboxName(), 0, SettingsStore.messagesPerPage(), '', '', true);
|
||||
Remote.messageList(noop, getFolderInboxName(), 0, SettingsStore.messagesPerPage(), '', '', true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -246,7 +259,7 @@ class AppUser extends AbstractApp
|
|||
const
|
||||
isSpam = sSpamFolder === item.To,
|
||||
isTrash = sTrashFolder === item.To,
|
||||
isHam = !isSpam && sSpamFolder === item.From && Cache.getFolderInboxName() === item.To;
|
||||
isHam = !isSpam && sSpamFolder === item.From && getFolderInboxName() === item.To;
|
||||
|
||||
Remote.messagesMove(this.moveOrDeleteResponseHelper, item.From, item.To, item.Uid,
|
||||
isSpam ? 'SPAM' : (isHam ? 'HAM' : ''), isSpam || isTrash);
|
||||
|
@ -294,11 +307,11 @@ class AppUser extends AbstractApp
|
|||
{
|
||||
if (oData && isArray(oData.Result) && 2 === oData.Result.length)
|
||||
{
|
||||
Cache.setFolderHash(oData.Result[0], oData.Result[1]);
|
||||
setFolderHash(oData.Result[0], oData.Result[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
Cache.setFolderHash(FolderStore.currentFolderFullNameRaw(), '');
|
||||
setFolderHash(FolderStore.currentFolderFullNameRaw(), '');
|
||||
|
||||
if (oData && -1 < inArray(oData.ErrorCode,
|
||||
[Notification.CantMoveMessage, Notification.CantCopyMessage]))
|
||||
|
@ -336,18 +349,18 @@ class AppUser extends AbstractApp
|
|||
switch (iDeleteType)
|
||||
{
|
||||
case FolderType.Spam:
|
||||
oMoveFolder = Cache.getFolderFromCacheList(FolderStore.spamFolder());
|
||||
oMoveFolder = getFolderFromCacheList(FolderStore.spamFolder());
|
||||
nSetSystemFoldersNotification = SetSystemFoldersNotification.Spam;
|
||||
break;
|
||||
case FolderType.NotSpam:
|
||||
oMoveFolder = Cache.getFolderFromCacheList(Cache.getFolderInboxName());
|
||||
oMoveFolder = getFolderFromCacheList(getFolderInboxName());
|
||||
break;
|
||||
case FolderType.Trash:
|
||||
oMoveFolder = Cache.getFolderFromCacheList(FolderStore.trashFolder());
|
||||
oMoveFolder = getFolderFromCacheList(FolderStore.trashFolder());
|
||||
nSetSystemFoldersNotification = SetSystemFoldersNotification.Trash;
|
||||
break;
|
||||
case FolderType.Archive:
|
||||
oMoveFolder = Cache.getFolderFromCacheList(FolderStore.archiveFolder());
|
||||
oMoveFolder = getFolderFromCacheList(FolderStore.archiveFolder());
|
||||
nSetSystemFoldersNotification = SetSystemFoldersNotification.Archive;
|
||||
break;
|
||||
// no default
|
||||
|
@ -394,8 +407,8 @@ class AppUser extends AbstractApp
|
|||
if (sFromFolderFullNameRaw !== sToFolderFullNameRaw && isArray(aUidForMove) && 0 < aUidForMove.length)
|
||||
{
|
||||
const
|
||||
oFromFolder = Cache.getFolderFromCacheList(sFromFolderFullNameRaw),
|
||||
oToFolder = Cache.getFolderFromCacheList(sToFolderFullNameRaw);
|
||||
oFromFolder = getFolderFromCacheList(sFromFolderFullNameRaw),
|
||||
oToFolder = getFolderFromCacheList(sToFolderFullNameRaw);
|
||||
|
||||
if (oFromFolder && oToFolder)
|
||||
{
|
||||
|
@ -649,14 +662,14 @@ class AppUser extends AbstractApp
|
|||
check = false,
|
||||
unreadCountChange = false;
|
||||
|
||||
const folderFromCache = Cache.getFolderFromCacheList(data.Result.Folder);
|
||||
const folderFromCache = getFolderFromCacheList(data.Result.Folder);
|
||||
if (folderFromCache)
|
||||
{
|
||||
folderFromCache.interval = Momentor.momentNowUnix();
|
||||
folderFromCache.interval = momentNowUnix();
|
||||
|
||||
if (data.Result.Hash)
|
||||
{
|
||||
Cache.setFolderHash(data.Result.Folder, data.Result.Hash);
|
||||
setFolderHash(data.Result.Folder, data.Result.Hash);
|
||||
}
|
||||
|
||||
if (isNormal(data.Result.MessageCount))
|
||||
|
@ -676,7 +689,7 @@ class AppUser extends AbstractApp
|
|||
|
||||
if (unreadCountChange)
|
||||
{
|
||||
Cache.clearMessageFlagsFromCacheByFolder(folderFromCache.fullNameRaw);
|
||||
clearMessageFlagsFromCacheByFolder(folderFromCache.fullNameRaw);
|
||||
}
|
||||
|
||||
if (data.Result.Flags)
|
||||
|
@ -687,7 +700,7 @@ class AppUser extends AbstractApp
|
|||
{
|
||||
check = true;
|
||||
const flags = data.Result.Flags[uid];
|
||||
Cache.storeMessageFlagsToCacheByFolderAndUid(folderFromCache.fullNameRaw, uid.toString(), [
|
||||
storeMessageFlagsToCacheByFolderAndUid(folderFromCache.fullNameRaw, uid.toString(), [
|
||||
!flags.IsSeen, !!flags.IsFlagged, !!flags.IsAnswered, !!flags.IsForwarded, !!flags.IsReadReceipt
|
||||
]);
|
||||
}
|
||||
|
@ -701,14 +714,14 @@ class AppUser extends AbstractApp
|
|||
|
||||
MessageStore.initUidNextAndNewMessages(folderFromCache.fullNameRaw, data.Result.UidNext, data.Result.NewMessages);
|
||||
|
||||
const hash = Cache.getFolderHash(data.Result.Folder);
|
||||
const hash = getFolderHash(data.Result.Folder);
|
||||
if (data.Result.Hash !== hash || '' === hash || unreadCountChange)
|
||||
{
|
||||
if (folderFromCache.fullNameRaw === FolderStore.currentFolderFullNameRaw())
|
||||
{
|
||||
this.reloadMessageList();
|
||||
}
|
||||
else if (Cache.getFolderInboxName() === folderFromCache.fullNameRaw)
|
||||
else if (getFolderInboxName() === folderFromCache.fullNameRaw)
|
||||
{
|
||||
this.recacheInboxMessageList();
|
||||
}
|
||||
|
@ -733,12 +746,12 @@ class AppUser extends AbstractApp
|
|||
{
|
||||
if (oData && oData.Result && oData.Result.List && isNonEmptyArray(oData.Result.List))
|
||||
{
|
||||
const utc = Momentor.momentNowUnix();
|
||||
const utc = momentNowUnix();
|
||||
_.each(oData.Result.List, (oItem) => {
|
||||
|
||||
const
|
||||
hash = Cache.getFolderHash(oItem.Folder),
|
||||
folder = Cache.getFolderFromCacheList(oItem.Folder);
|
||||
hash = getFolderHash(oItem.Folder),
|
||||
folder = getFolderFromCacheList(oItem.Folder);
|
||||
let
|
||||
unreadCountChange = false;
|
||||
|
||||
|
@ -748,7 +761,7 @@ class AppUser extends AbstractApp
|
|||
|
||||
if (oItem.Hash)
|
||||
{
|
||||
Cache.setFolderHash(oItem.Folder, oItem.Hash);
|
||||
setFolderHash(oItem.Folder, oItem.Hash);
|
||||
}
|
||||
|
||||
if (isNormal(oItem.MessageCount))
|
||||
|
@ -768,7 +781,7 @@ class AppUser extends AbstractApp
|
|||
|
||||
if (unreadCountChange)
|
||||
{
|
||||
Cache.clearMessageFlagsFromCacheByFolder(folder.fullNameRaw);
|
||||
clearMessageFlagsFromCacheByFolder(folder.fullNameRaw);
|
||||
}
|
||||
|
||||
if (oItem.Hash !== hash || '' === hash)
|
||||
|
@ -829,11 +842,10 @@ class AppUser extends AbstractApp
|
|||
case MessageSetAction.SetSeen:
|
||||
|
||||
_.each(rootUids, (sSubUid) => {
|
||||
alreadyUnread += Cache.storeMessageFlagsToCacheBySetAction(
|
||||
sFolderFullNameRaw, sSubUid, iSetAction);
|
||||
alreadyUnread += storeMessageFlagsToCacheBySetAction(sFolderFullNameRaw, sSubUid, iSetAction);
|
||||
});
|
||||
|
||||
folder = Cache.getFolderFromCacheList(sFolderFullNameRaw);
|
||||
folder = getFolderFromCacheList(sFolderFullNameRaw);
|
||||
if (folder)
|
||||
{
|
||||
folder.messageCountUnread(folder.messageCountUnread() - alreadyUnread);
|
||||
|
@ -845,11 +857,11 @@ class AppUser extends AbstractApp
|
|||
case MessageSetAction.UnsetSeen:
|
||||
|
||||
_.each(rootUids, (sSubUid) => {
|
||||
alreadyUnread += Cache.storeMessageFlagsToCacheBySetAction(
|
||||
alreadyUnread += storeMessageFlagsToCacheBySetAction(
|
||||
sFolderFullNameRaw, sSubUid, iSetAction);
|
||||
});
|
||||
|
||||
folder = Cache.getFolderFromCacheList(sFolderFullNameRaw);
|
||||
folder = getFolderFromCacheList(sFolderFullNameRaw);
|
||||
if (folder)
|
||||
{
|
||||
folder.messageCountUnread(folder.messageCountUnread() - alreadyUnread + rootUids.length);
|
||||
|
@ -861,8 +873,7 @@ class AppUser extends AbstractApp
|
|||
case MessageSetAction.SetFlag:
|
||||
|
||||
_.each(rootUids, (sSubUid) => {
|
||||
Cache.storeMessageFlagsToCacheBySetAction(
|
||||
sFolderFullNameRaw, sSubUid, iSetAction);
|
||||
storeMessageFlagsToCacheBySetAction(sFolderFullNameRaw, sSubUid, iSetAction);
|
||||
});
|
||||
|
||||
Remote.messageSetFlagged(noop, sFolderFullNameRaw, rootUids, true);
|
||||
|
@ -871,8 +882,7 @@ class AppUser extends AbstractApp
|
|||
case MessageSetAction.UnsetFlag:
|
||||
|
||||
_.each(rootUids, (sSubUid) => {
|
||||
Cache.storeMessageFlagsToCacheBySetAction(
|
||||
sFolderFullNameRaw, sSubUid, iSetAction);
|
||||
storeMessageFlagsToCacheBySetAction(sFolderFullNameRaw, sSubUid, iSetAction);
|
||||
});
|
||||
|
||||
Remote.messageSetFlagged(noop, sFolderFullNameRaw, rootUids, false);
|
||||
|
@ -886,15 +896,15 @@ class AppUser extends AbstractApp
|
|||
}
|
||||
|
||||
googleConnect() {
|
||||
window.open(Links.socialGoogle(), 'Google', 'left=200,top=100,width=650,height=600,menubar=no,status=no,resizable=yes,scrollbars=yes');
|
||||
window.open(socialGoogle(), 'Google', 'left=200,top=100,width=650,height=600,menubar=no,status=no,resizable=yes,scrollbars=yes');
|
||||
}
|
||||
|
||||
twitterConnect() {
|
||||
window.open(Links.socialTwitter(), 'Twitter', 'left=200,top=100,width=650,height=350,menubar=no,status=no,resizable=yes,scrollbars=yes');
|
||||
window.open(socialTwitter(), 'Twitter', 'left=200,top=100,width=650,height=350,menubar=no,status=no,resizable=yes,scrollbars=yes');
|
||||
}
|
||||
|
||||
facebookConnect() {
|
||||
window.open(Links.socialFacebook(), 'Facebook', 'left=200,top=100,width=650,height=335,menubar=no,status=no,resizable=yes,scrollbars=yes');
|
||||
window.open(socialFacebook(), 'Facebook', 'left=200,top=100,width=650,height=335,menubar=no,status=no,resizable=yes,scrollbars=yes');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1221,13 +1231,13 @@ class AppUser extends AbstractApp
|
|||
LoginUserScreen
|
||||
]);
|
||||
|
||||
Plugins.runHook('rl-start-login-screens');
|
||||
runHook('rl-start-login-screens');
|
||||
Events.pub('rl.bootstart-login-screens');
|
||||
}
|
||||
else
|
||||
{
|
||||
routeOff();
|
||||
setHash(Links.root(), true);
|
||||
setHash(root(), true);
|
||||
routeOff();
|
||||
|
||||
_.defer(() => {
|
||||
|
@ -1300,7 +1310,7 @@ class AppUser extends AbstractApp
|
|||
if ('' !== startupUrl)
|
||||
{
|
||||
routeOff();
|
||||
setHash(Links.root(startupUrl), true);
|
||||
setHash(root(startupUrl), true);
|
||||
routeOn();
|
||||
}
|
||||
|
||||
|
@ -1314,7 +1324,7 @@ class AppUser extends AbstractApp
|
|||
{
|
||||
try
|
||||
{
|
||||
PgpStore.openpgp.initWorker({path: Links.openPgpWorkerJs()});
|
||||
PgpStore.openpgp.initWorker({path: openPgpWorkerJs()});
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
|
@ -1336,7 +1346,7 @@ class AppUser extends AbstractApp
|
|||
}
|
||||
else
|
||||
{
|
||||
jassl(Links.openPgpJs()).then(() => {
|
||||
jassl(openPgpJs()).then(() => {
|
||||
if (window.openpgp)
|
||||
{
|
||||
openpgpCallback(window.openpgp);
|
||||
|
@ -1360,10 +1370,10 @@ class AppUser extends AbstractApp
|
|||
this.socialUsers(true);
|
||||
}
|
||||
|
||||
Events.sub('interval.2m', () => this.folderInformation(Cache.getFolderInboxName()));
|
||||
Events.sub('interval.2m', () => this.folderInformation(getFolderInboxName()));
|
||||
Events.sub('interval.3m', () => {
|
||||
const sF = FolderStore.currentFolderFullNameRaw();
|
||||
if (Cache.getFolderInboxName() !== sF)
|
||||
if (getFolderInboxName() !== sF)
|
||||
{
|
||||
this.folderInformation(sF);
|
||||
}
|
||||
|
@ -1385,7 +1395,7 @@ class AppUser extends AbstractApp
|
|||
|
||||
_.delay(() => {
|
||||
const sF = FolderStore.currentFolderFullNameRaw();
|
||||
if (Cache.getFolderInboxName() !== sF)
|
||||
if (getFolderInboxName() !== sF)
|
||||
{
|
||||
this.folderInformation(sF);
|
||||
}
|
||||
|
@ -1396,7 +1406,7 @@ class AppUser extends AbstractApp
|
|||
|
||||
Events.sub('rl.auto-logout', () => this.logout());
|
||||
|
||||
Plugins.runHook('rl-start-user-screens');
|
||||
runHook('rl-start-user-screens');
|
||||
Events.pub('rl.bootstart-user-screens');
|
||||
|
||||
if (Settings.settingsGet('WelcomePageUrl'))
|
||||
|
@ -1478,9 +1488,9 @@ class AppUser extends AbstractApp
|
|||
};
|
||||
}
|
||||
|
||||
Events.sub('interval.1m', () => Momentor.reload());
|
||||
Events.sub('interval.1m', () => momentReload());
|
||||
|
||||
Plugins.runHook('rl-start-screens');
|
||||
runHook('rl-start-screens');
|
||||
Events.pub('rl.bootstart-end');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,315 @@
|
|||
|
||||
// import window from 'window';
|
||||
import window from 'window';
|
||||
import $ from '$';
|
||||
import _ from '_';
|
||||
import {$body} from 'Common/Globals';
|
||||
import ko from 'ko';
|
||||
import {$html, $body} from 'Common/Globals';
|
||||
import {EventKeyCode} from 'Common/Enums';
|
||||
import {trim, inArray, changeTheme} from 'Common/Utils';
|
||||
import {reload as translatorReload} from 'Common/Translator';
|
||||
|
||||
import * as Settings from 'Storage/Settings';
|
||||
|
||||
import ThemeStore from 'Stores/Theme';
|
||||
import LanguageStore from 'Stores/Language';
|
||||
|
||||
let
|
||||
opened = false,
|
||||
cmdDom = null;
|
||||
cmdDom = null,
|
||||
contoller = null;
|
||||
|
||||
/**
|
||||
* @params {string} cmd
|
||||
* @returns {string}
|
||||
*/
|
||||
function cmdError(cmd) {
|
||||
return require('Html/Cmds/Error.html').replace('{{ cmd }}', cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string}
|
||||
*/
|
||||
function cmdClear(dom) {
|
||||
dom.find('.rl-cmd-history-data').empty();
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string}
|
||||
*/
|
||||
function cmdHelp(cmds) {
|
||||
return require('Html/Cmds/Help.html').replace('{{ commands }}', cmds.join(' '));
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string}
|
||||
*/
|
||||
function cmdGlass() {
|
||||
$html.toggleClass('glass', !$html.hasClass('glass'));
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string}
|
||||
*/
|
||||
function cmdTheme(param, themes) {
|
||||
if (param && -1 < inArray(param, themes))
|
||||
{
|
||||
changeTheme(param);
|
||||
return '';
|
||||
}
|
||||
return require('Html/Cmds/ThemeEmpty.html').replace('{{ themes }}', themes.join(', '));
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string}
|
||||
*/
|
||||
function cmdLang(param, isAdmin, langs) {
|
||||
if (param && -1 < inArray(param, langs))
|
||||
{
|
||||
translatorReload(isAdmin, param);
|
||||
return '';
|
||||
}
|
||||
return require('Html/Cmds/LangEmpty.html').replace('{{ langs }}', langs.join(', '));
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string}
|
||||
*/
|
||||
function cmdVersion() {
|
||||
return require('Html/Cmds/Version.html').replace('{{ version }}',
|
||||
Settings.appSettingsGet('version') + ' (' + Settings.appSettingsGet('appVersionType') + ')');
|
||||
}
|
||||
|
||||
class CmdContoller
|
||||
{
|
||||
constructor(dom)
|
||||
{
|
||||
this.dom = dom;
|
||||
|
||||
this.opened = ko.observable(false);
|
||||
this.cmd = ko.observable('');
|
||||
this.focused = ko.observable(false);
|
||||
|
||||
this.themes = ThemeStore.themes;
|
||||
|
||||
this.cmdHistory = [];
|
||||
this.cmdHistoryShift = 0;
|
||||
|
||||
this.cmdHelper = ko.observable('');
|
||||
|
||||
this.cmds = ['help', 'version', 'glass', 'clear', 'theme', 'lang'];
|
||||
this.cmdsWithParameters = ['theme', 'lang'];
|
||||
|
||||
this.isAdmin = Settings.appSettingsGet('admin');
|
||||
}
|
||||
|
||||
runCmd(cmd, params, isTab) {
|
||||
|
||||
let
|
||||
result = '',
|
||||
values = null;
|
||||
|
||||
this.cmdHelper('');
|
||||
|
||||
if (isTab)
|
||||
{
|
||||
switch (cmd) {
|
||||
case 'lang':
|
||||
values = (this.isAdmin ? LanguageStore.languagesAdmin() : LanguageStore.languages())
|
||||
.filter((line) => 0 === line.lastIndexOf(params, 0));
|
||||
break;
|
||||
case 'theme':
|
||||
values = ThemeStore.themes().filter((line) => 0 === line.lastIndexOf(params, 0));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (cmd && values)
|
||||
{
|
||||
if (1 === values.length && values[0])
|
||||
{
|
||||
this.cmd(cmd + ' ' + values[0]);
|
||||
}
|
||||
else if (1 < values.length && values[0] && values[1])
|
||||
{
|
||||
let
|
||||
sub = '',
|
||||
index = 0;
|
||||
|
||||
const
|
||||
list = values[0].split(''),
|
||||
len = list.length;
|
||||
|
||||
for (; index < len; index++)
|
||||
{
|
||||
if (values[1][index] === list[index])
|
||||
{
|
||||
sub += list[index];
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (sub)
|
||||
{
|
||||
this.cmdHelper('[' + values.join(', ') + ']');
|
||||
this.cmd(cmd + ' ' + sub);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case 'hi':
|
||||
result = 'hello';
|
||||
break;
|
||||
case '?':
|
||||
case 'ls':
|
||||
case 'help':
|
||||
result = cmdHelp(this.cmds);
|
||||
break;
|
||||
case 'glass':
|
||||
result = cmdGlass();
|
||||
break;
|
||||
case 'v':
|
||||
case 'version':
|
||||
result = cmdVersion();
|
||||
break;
|
||||
case 'clear':
|
||||
result = cmdClear(this.dom);
|
||||
break;
|
||||
case 'theme':
|
||||
result = cmdTheme(params, ThemeStore.themes());
|
||||
break;
|
||||
case 'lang':
|
||||
result = cmdLang(params, this.isAdmin, this.isAdmin ? LanguageStore.languagesAdmin() : LanguageStore.languages());
|
||||
break;
|
||||
default:
|
||||
result = cmdError(cmd);
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
onCmd(isTab) {
|
||||
const
|
||||
cmdLine = trim(this.cmd()).replace(/[\s]+/, ' '),
|
||||
cmdParts = cmdLine.replace().split(/[\s]+/),
|
||||
cmd = cmdParts.shift();
|
||||
|
||||
if (isTab)
|
||||
{
|
||||
if (-1 < inArray(cmd, this.cmds))
|
||||
{
|
||||
const result = this.runCmd(cmd, cmdParts.join(' '), true);
|
||||
if (result)
|
||||
{
|
||||
this.cmd(result);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const values = this.cmds.filter((line) => line !== cmd && 0 === line.lastIndexOf(cmd, 0));
|
||||
if (1 === values.length && values[0])
|
||||
{
|
||||
this.cmd(values[0] + (-1 < inArray(values[0], this.cmdsWithParameters) ? ' ' : ''));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.cmdHistory.unshift(cmdLine);
|
||||
this.cmdHistory = _.uniq(this.cmdHistory);
|
||||
this.cmdHistoryShift = 0;
|
||||
|
||||
const
|
||||
result = this.runCmd(cmd, cmdParts.join(' '), false),
|
||||
h = this.dom.find('.rl-cmd-history-data');
|
||||
|
||||
if (h && h[0])
|
||||
{
|
||||
h.append($('<div></div>').html(require('Html/Cmds/Main.html').replace('{{ cmd }}', cmdLine)));
|
||||
if (result)
|
||||
{
|
||||
h.append($('<div></div>').html(result));
|
||||
}
|
||||
|
||||
_.delay(() => {
|
||||
this.dom.find('.rl-cmd-history').scrollTop(h.height());
|
||||
}, 50);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onEsc() {
|
||||
this.opened(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
onTab() {
|
||||
this.onCmd(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
onEnter() {
|
||||
this.onCmd(false);
|
||||
this.cmd('');
|
||||
return false;
|
||||
}
|
||||
|
||||
onKeyDown(event) {
|
||||
if (event && event.keyCode &&
|
||||
!event.metaKey && !event.ctrlKey && !event.shiftKey && 0 < this.cmdHistory.length)
|
||||
{
|
||||
const code = window.parseInt(event.keyCode, 10);
|
||||
if (EventKeyCode.Up === code || EventKeyCode.Down === code)
|
||||
{
|
||||
if (this.cmdHistory[this.cmdHistoryShift])
|
||||
{
|
||||
this.cmd(this.cmdHistory[this.cmdHistoryShift]);
|
||||
if (EventKeyCode.Up === code)
|
||||
{
|
||||
this.cmdHistoryShift += 1;
|
||||
}
|
||||
else if (EventKeyCode.Down === code)
|
||||
{
|
||||
this.cmdHistoryShift -= 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.cmdHistoryShift = 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {void}
|
||||
*/
|
||||
export function bind(dom)
|
||||
{
|
||||
if (!contoller)
|
||||
{
|
||||
contoller = new CmdContoller(dom);
|
||||
|
||||
ko.applyBindingAccessorsToNode(dom[0], {
|
||||
translatorInit: true,
|
||||
template: () => ({name: 'Cmd'})
|
||||
}, contoller);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {void}
|
||||
|
@ -15,8 +318,10 @@ function init()
|
|||
{
|
||||
if (null === cmdDom)
|
||||
{
|
||||
cmdDom = $('<div class="rl-cmd"></div>');
|
||||
cmdDom = $('<div></div>');
|
||||
cmdDom.appendTo($body);
|
||||
|
||||
bind(cmdDom);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,11 +330,21 @@ function init()
|
|||
*/
|
||||
export function toggle()
|
||||
{
|
||||
init();
|
||||
if (Settings.appSettingsGet('allowCmdInterface'))
|
||||
{
|
||||
init();
|
||||
|
||||
opened = !opened;
|
||||
|
||||
_.delay(() => {
|
||||
cmdDom.toggleClass('opened', opened);
|
||||
}, 50);
|
||||
_.delay(() => {
|
||||
if (contoller)
|
||||
{
|
||||
contoller.opened(!contoller.opened());
|
||||
if (contoller.opened())
|
||||
{
|
||||
_.delay(() => {
|
||||
contoller.focused(true);
|
||||
}, 50);
|
||||
}
|
||||
}
|
||||
}, 50);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -312,7 +312,7 @@ export function reload(admin, language)
|
|||
|
||||
reloadData();
|
||||
|
||||
const isRtl = -1 < inArray(language, ['ar', 'ar_sa', 'he', 'he_he', 'ur', 'ur_ir']);
|
||||
const isRtl = -1 < inArray((language || '').toLowerCase(), ['ar', 'ar_sa', 'he', 'he_he', 'ur', 'ur_ir']);
|
||||
|
||||
$html
|
||||
.removeClass('rl-changing-language')
|
||||
|
|
|
@ -1239,10 +1239,10 @@ let
|
|||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @param {function} themeTrigger
|
||||
* @param {function=} themeTrigger = noop
|
||||
* @returns {void}
|
||||
*/
|
||||
export function changeTheme(value, themeTrigger)
|
||||
export function changeTheme(value, themeTrigger = noop)
|
||||
{
|
||||
const
|
||||
themeLink = $('#app-theme-link'),
|
||||
|
@ -1272,6 +1272,7 @@ export function changeTheme(value, themeTrigger)
|
|||
}
|
||||
|
||||
window.clearTimeout(__themeTimer);
|
||||
|
||||
themeTrigger(SaveSettingsStep.Animate);
|
||||
|
||||
if (__themeAjax && __themeAjax.abort)
|
||||
|
|
21
dev/External/ko.js
vendored
21
dev/External/ko.js
vendored
|
@ -382,6 +382,23 @@ ko.bindingHandlers.resizecrop = {
|
|||
}
|
||||
};
|
||||
|
||||
ko.bindingHandlers.onKeyDown = {
|
||||
init: (element, fValueAccessor, fAllBindingsAccessor, viewModel) => {
|
||||
$(element).on('keydown.koOnKeyDown', (event) => {
|
||||
if (event)
|
||||
{
|
||||
return fValueAccessor().call(viewModel, event);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
ko.utils.domNodeDisposal.addDisposeCallback(element, () => {
|
||||
$(element).off('keydown.koOnKeyDown');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
ko.bindingHandlers.onEnter = {
|
||||
init: (element, fValueAccessor, fAllBindingsAccessor, viewModel) => {
|
||||
$(element).on('keypress.koOnEnter', (event) => {
|
||||
|
@ -431,7 +448,7 @@ ko.bindingHandlers.onTab = {
|
|||
|
||||
ko.bindingHandlers.onEsc = {
|
||||
init: (element, fValueAccessor, fAllBindingsAccessor, viewModel) => {
|
||||
$(element).on('keypress.koOnEsc', (event) => {
|
||||
$(element).on('keyup.koOnEsc', (event) => {
|
||||
if (event && 27 === window.parseInt(event.keyCode, 10))
|
||||
{
|
||||
$(element).trigger('change');
|
||||
|
@ -440,7 +457,7 @@ ko.bindingHandlers.onEsc = {
|
|||
});
|
||||
|
||||
ko.utils.domNodeDisposal.addDisposeCallback(element, () => {
|
||||
$(element).off('keypress.koOnEsc');
|
||||
$(element).off('keyup.koOnEsc');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
1
dev/Html/Cmds/Done.html
Normal file
1
dev/Html/Cmds/Done.html
Normal file
|
@ -0,0 +1 @@
|
|||
<span class="rl-cmd-clr-success">done!</span>
|
1
dev/Html/Cmds/Error.html
Normal file
1
dev/Html/Cmds/Error.html
Normal file
|
@ -0,0 +1 @@
|
|||
<span class="rl-cmd-clr-error">Command not found:</span> {{ cmd }}
|
1
dev/Html/Cmds/Help.html
Normal file
1
dev/Html/Cmds/Help.html
Normal file
|
@ -0,0 +1 @@
|
|||
<span class="rl-cmd-clr-info">commands:</span> {{ commands }}
|
1
dev/Html/Cmds/LangEmpty.html
Normal file
1
dev/Html/Cmds/LangEmpty.html
Normal file
|
@ -0,0 +1 @@
|
|||
lang <span class="rl-cmd-clr-info">[{{ langs }}]</span>
|
1
dev/Html/Cmds/Main.html
Normal file
1
dev/Html/Cmds/Main.html
Normal file
|
@ -0,0 +1 @@
|
|||
<span class="rl-cmd-clr-success">></span> {{ cmd }}
|
1
dev/Html/Cmds/ThemeEmpty.html
Normal file
1
dev/Html/Cmds/ThemeEmpty.html
Normal file
|
@ -0,0 +1 @@
|
|||
theme <span class="rl-cmd-clr-info">[{{ themes }}]</span>
|
1
dev/Html/Cmds/Version.html
Normal file
1
dev/Html/Cmds/Version.html
Normal file
|
@ -0,0 +1 @@
|
|||
<span class="rl-cmd-clr-info">version:</span> {{ version }}
|
|
@ -7,12 +7,75 @@
|
|||
top: auto;
|
||||
height: 0;
|
||||
|
||||
background: rgba(0, 0, 0, .8);
|
||||
background: rgba(0, 0, 0, .85);
|
||||
border-top: 1px solid #000;
|
||||
|
||||
overflow: hidden;
|
||||
font-family: monospace;
|
||||
|
||||
.transition(height 0.1s ease-out);
|
||||
|
||||
&.opened {
|
||||
height: 200px;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.rl-cmd-clr-error {
|
||||
color: #CD3131;
|
||||
}
|
||||
|
||||
.rl-cmd-clr-info {
|
||||
color: #BFBF00;
|
||||
}
|
||||
|
||||
.rl-cmd-clr-success {
|
||||
color: #31FF40;
|
||||
}
|
||||
|
||||
.rl-cmd-wrp {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.rl-cmd-input-helper {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.rl-cmd-input-prefix {
|
||||
color: #31FF40;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.rl-cmd-input-wrp {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.rl-cmd-input {
|
||||
background: none;
|
||||
border: none;
|
||||
color: white;
|
||||
display: inline-block;
|
||||
width: calc(~'100% - 30px');
|
||||
font-family: monospace;
|
||||
|
||||
&:focus{
|
||||
background: none;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.rl-cmd-history {
|
||||
color: white;
|
||||
font-family: monospace;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
bottom: 60px;
|
||||
left: 10px;
|
||||
right: -30px;
|
||||
overflow: hidden;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -431,11 +431,11 @@ class LoginUserView extends AbstractViewNext
|
|||
|
||||
_.delay(() => {
|
||||
|
||||
LanguageStore.language.subscribe((sValue) => {
|
||||
LanguageStore.language.subscribe((value) => {
|
||||
|
||||
this.langRequest(true);
|
||||
|
||||
translatorReload(false, sValue).then(() => {
|
||||
translatorReload(false, value).then(() => {
|
||||
this.langRequest(false);
|
||||
this.bSendLanguage = true;
|
||||
}, () => {
|
||||
|
|
|
@ -491,10 +491,9 @@ gulp.task('rainloop:setup', ['rainloop:copy'], function() {
|
|||
fs.writeFileSync(dist + 'data/EMPTY', versionFull);
|
||||
|
||||
fs.writeFileSync(dist + 'index.php', fs.readFileSync('index.php', 'utf8')
|
||||
.replace('\'APP_VERSION\', \'0.0.0\'', '\'APP_VERSION\', \'' + versionFull + '\''));
|
||||
|
||||
fs.writeFileSync(dist + 'index.php', fs.readFileSync('index.php', 'utf8')
|
||||
.replace('\'APP_VERSION_TYPE\', \'source\'', '\'APP_VERSION\', \'' + (cfg.community ? 'community' : 'standard') + '\''));
|
||||
.replace('\'APP_VERSION\', \'0.0.0\'', '\'APP_VERSION\', \'' + versionFull + '\'')
|
||||
.replace('\'APP_VERSION_TYPE\', \'source\'', '\'APP_VERSION_TYPE\', \'' + (cfg.community ? 'community' : 'standard') + '\'')
|
||||
);
|
||||
|
||||
fs.writeFileSync(dist + 'rainloop/v/' + versionFull + '/index.php.root', fs.readFileSync(dist + 'index.php'));
|
||||
|
||||
|
|
|
@ -1441,6 +1441,7 @@ class Actions
|
|||
|
||||
return \array_merge(array(
|
||||
'version' => APP_VERSION,
|
||||
'admin' => $bAdmin,
|
||||
'mobile' => $bMobile,
|
||||
'mobileDevice' => $bMobileDevice,
|
||||
'webPath' => \RainLoop\Utils::WebPath(),
|
||||
|
@ -1461,11 +1462,13 @@ class Actions
|
|||
'materialDesign' => (bool) $oConfig->Get('labs', 'use_material_design', true),
|
||||
'folderSpecLimit' => (int) $oConfig->Get('labs', 'folders_spec_limit', 50),
|
||||
'faviconStatus' => (bool) $oConfig->Get('labs', 'favicon_status', true),
|
||||
'allowCmdInterface' => (bool) $oConfig->Get('labs', 'allow_cmd', false),
|
||||
'useNativeScrollbars' => (bool) $oConfig->Get('interface', 'use_native_scrollbars', false),
|
||||
'listPermanentFiltered' => '' !== \trim(\RainLoop\Api::Config()->Get('labs', 'imap_message_list_permanent_filter', '')),
|
||||
'themes' => $this->GetThemes($bMobile, false),
|
||||
'languages' => $this->GetLanguages(false),
|
||||
'languagesAdmin' => $this->GetLanguages(true),
|
||||
'appVersionType' => APP_VERSION_TYPE,
|
||||
'attachmentsActions' => $aAttachmentsActions
|
||||
), $bAdmin ? array(
|
||||
'adminHostUse' => '' !== $oConfig->Get('security', 'admin_panel_host', ''),
|
||||
|
|
|
@ -446,6 +446,7 @@ Enables caching in the system'),
|
|||
'startup_url' => array(''),
|
||||
'nice_social_redirect' => array(true),
|
||||
'strict_html_parser' => array(false),
|
||||
'allow_cmd' => array(false),
|
||||
'dev_email' => array(''),
|
||||
'dev_password' => array('')
|
||||
),
|
||||
|
|
10
rainloop/v/0.0.0/app/templates/Views/Common/Cmd.html
Normal file
10
rainloop/v/0.0.0/app/templates/Views/Common/Cmd.html
Normal file
|
@ -0,0 +1,10 @@
|
|||
<div class="rl-cmd" data-bind="css: {opened: opened}, click: function() { focused(true) }">
|
||||
<div class="rl-cmd-wrp">
|
||||
<div class="rl-cmd-history"><div class="rl-cmd-history-data"></div></div>
|
||||
<div class="rl-cmd-input-wrp">
|
||||
<div class="rl-cmd-input-helper" data-bind="text: cmdHelper"></div>
|
||||
<div class="rl-cmd-input-prefix">#</div>
|
||||
<input type="text" class="rl-cmd-input" data-bind="textInput: cmd, hasFocus: focused, onEnter: onEnter, onKeyDown: onKeyDown, onEsc: onEsc, onTab: onTab" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,26 +1,26 @@
|
|||
<div class="popups">
|
||||
<div class="modal hide b-ask-content g-ui-user-select-none" data-bind="modal: modalVisibility">
|
||||
<div>
|
||||
<div class="modal-body">
|
||||
<br />
|
||||
<br />
|
||||
<span class="desc-place" data-bind="html: askDesc"></span>
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn buttonYes" data-bind="click: yesClick, hasFocus: yesFocus">
|
||||
<i class="icon-ok"></i>
|
||||
|
||||
<span data-bind="text: yesButton"></span>
|
||||
</button>
|
||||
<button class="btn buttonNo" data-bind="click: noClick, hasFocus: noFocus">
|
||||
<i class=" icon-remove"></i>
|
||||
|
||||
<span data-bind="text: noButton"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="popups">
|
||||
<div class="modal hide b-ask-content g-ui-user-select-none" data-bind="modal: modalVisibility">
|
||||
<div>
|
||||
<div class="modal-body">
|
||||
<br />
|
||||
<br />
|
||||
<span class="desc-place" data-bind="html: askDesc"></span>
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn buttonYes" data-bind="click: yesClick, hasFocus: yesFocus">
|
||||
<i class="icon-ok"></i>
|
||||
|
||||
<span data-bind="text: yesButton"></span>
|
||||
</button>
|
||||
<button class="btn buttonNo" data-bind="click: noClick, hasFocus: noFocus">
|
||||
<i class=" icon-remove"></i>
|
||||
|
||||
<span data-bind="text: noButton"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -65,7 +65,7 @@ module.exports = function(publicPath, pro, es6) {
|
|||
"transform-es2015-function-name",
|
||||
// ["transform-es2015-arrow-functions")],
|
||||
"transform-es2015-block-scoped-functions",
|
||||
// ["transform-es2015-classes", loose],
|
||||
// ["transform-es2015-classes", {loose: true}],
|
||||
// "transform-es2015-object-super",
|
||||
"transform-es2015-shorthand-properties",
|
||||
"transform-es2015-duplicate-keys",
|
||||
|
@ -104,7 +104,7 @@ module.exports = function(publicPath, pro, es6) {
|
|||
|
||||
// other
|
||||
'transform-runtime',
|
||||
'transform-decorators-legacy'
|
||||
'transform-decorators-legacy' // -> transform-decorators // from stage-2
|
||||
]
|
||||
}
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue