Add cmd interface

This commit is contained in:
RainLoop Team 2016-08-22 00:30:34 +03:00
parent 17669b7be0
commit e6e1a19477
22 changed files with 540 additions and 111 deletions

View file

@ -115,9 +115,12 @@ class AbstractApp extends AbstractBoot
detectDropdownVisibility(); detectDropdownVisibility();
}); });
key('ctrl+shift+`', KeyState.All, () => { if (Settings.appSettingsGet('allowCmdInterface'))
toggleCmd(); {
}); key('ctrl+shift+`', KeyState.All, () => {
toggleCmd();
});
}
} }
remote() { remote() {

View file

@ -4,7 +4,7 @@ import _ from '_';
import ko from 'ko'; import ko from 'ko';
import progressJs from 'progressJs'; import progressJs from 'progressJs';
import * as Links from 'Common/Links'; import {root} from 'Common/Links';
import {getNotification} from 'Common/Translator'; import {getNotification} from 'Common/Translator';
import {StorageResultType, Notification} from 'Common/Enums'; import {StorageResultType, Notification} from 'Common/Enums';
import {pInt, isNormal, isArray, inArray, isUnd} from 'Common/Utils'; import {pInt, isNormal, isArray, inArray, isUnd} from 'Common/Utils';
@ -225,7 +225,7 @@ class AdminApp extends AbstractApp
if (!Settings.appSettingsGet('allowAdminPanel')) if (!Settings.appSettingsGet('allowAdminPanel'))
{ {
routeOff(); routeOff();
setHash(Links.root(), true); setHash(root(), true);
routeOff(); routeOff();
_.defer(() => { _.defer(() => {

View file

@ -22,11 +22,24 @@ import {
} from 'Common/Globals'; } from 'Common/Globals';
import {UNUSED_OPTION_VALUE} from 'Common/Consts'; import {UNUSED_OPTION_VALUE} from 'Common/Consts';
import * as Plugins from 'Common/Plugins'; import {runHook} from 'Common/Plugins';
import * as Links from 'Common/Links'; 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 Events from 'Common/Events';
import * as Momentor from 'Common/Momentor';
import * as Cache from 'Common/Cache';
import {getNotification, i18n} from 'Common/Translator'; import {getNotification, i18n} from 'Common/Translator';
import SocialStore from 'Stores/Social'; import SocialStore from 'Stores/Social';
@ -114,7 +127,7 @@ class AppUser extends AbstractApp
_.delay(() => { _.delay(() => {
$('#rl-bg') $('#rl-bg')
.attr('style', 'background-image: none !important;') .attr('style', 'background-image: none !important;')
.backstretch(Links.userBackground(Settings.settingsGet('UserBackgroundHash')), { .backstretch(userBackground(Settings.settingsGet('UserBackgroundHash')), {
fade: bAnimationSupported ? 1000 : 0, fade: bAnimationSupported ? 1000 : 0,
centeredX: true, centeredX: true,
centeredY: true centeredY: true
@ -143,9 +156,9 @@ class AppUser extends AbstractApp
reloadFlagsCurrentMessageListAndMessageFromCache() { reloadFlagsCurrentMessageListAndMessageFromCache() {
_.each(MessageStore.messageList(), (message) => { _.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) if (bDropCurrenFolderCache)
{ {
Cache.setFolderHash(FolderStore.currentFolderFullNameRaw(), ''); setFolderHash(FolderStore.currentFolderFullNameRaw(), '');
} }
if (bDropPagePosition) if (bDropPagePosition)
@ -168,7 +181,7 @@ class AppUser extends AbstractApp
MessageStore.messageListPageBeforeThread(1); MessageStore.messageListPageBeforeThread(1);
iOffset = 0; iOffset = 0;
setHash(Links.mailBox( setHash(mailBox(
FolderStore.currentFolderFullNameHash(), FolderStore.currentFolderFullNameHash(),
MessageStore.messageListPage(), MessageStore.messageListPage(),
MessageStore.messageListSearch(), MessageStore.messageListSearch(),
@ -205,7 +218,7 @@ class AppUser extends AbstractApp
} }
recacheInboxMessageList() { 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 const
isSpam = sSpamFolder === item.To, isSpam = sSpamFolder === item.To,
isTrash = sTrashFolder === 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, Remote.messagesMove(this.moveOrDeleteResponseHelper, item.From, item.To, item.Uid,
isSpam ? 'SPAM' : (isHam ? 'HAM' : ''), isSpam || isTrash); isSpam ? 'SPAM' : (isHam ? 'HAM' : ''), isSpam || isTrash);
@ -294,11 +307,11 @@ class AppUser extends AbstractApp
{ {
if (oData && isArray(oData.Result) && 2 === oData.Result.length) 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 else
{ {
Cache.setFolderHash(FolderStore.currentFolderFullNameRaw(), ''); setFolderHash(FolderStore.currentFolderFullNameRaw(), '');
if (oData && -1 < inArray(oData.ErrorCode, if (oData && -1 < inArray(oData.ErrorCode,
[Notification.CantMoveMessage, Notification.CantCopyMessage])) [Notification.CantMoveMessage, Notification.CantCopyMessage]))
@ -336,18 +349,18 @@ class AppUser extends AbstractApp
switch (iDeleteType) switch (iDeleteType)
{ {
case FolderType.Spam: case FolderType.Spam:
oMoveFolder = Cache.getFolderFromCacheList(FolderStore.spamFolder()); oMoveFolder = getFolderFromCacheList(FolderStore.spamFolder());
nSetSystemFoldersNotification = SetSystemFoldersNotification.Spam; nSetSystemFoldersNotification = SetSystemFoldersNotification.Spam;
break; break;
case FolderType.NotSpam: case FolderType.NotSpam:
oMoveFolder = Cache.getFolderFromCacheList(Cache.getFolderInboxName()); oMoveFolder = getFolderFromCacheList(getFolderInboxName());
break; break;
case FolderType.Trash: case FolderType.Trash:
oMoveFolder = Cache.getFolderFromCacheList(FolderStore.trashFolder()); oMoveFolder = getFolderFromCacheList(FolderStore.trashFolder());
nSetSystemFoldersNotification = SetSystemFoldersNotification.Trash; nSetSystemFoldersNotification = SetSystemFoldersNotification.Trash;
break; break;
case FolderType.Archive: case FolderType.Archive:
oMoveFolder = Cache.getFolderFromCacheList(FolderStore.archiveFolder()); oMoveFolder = getFolderFromCacheList(FolderStore.archiveFolder());
nSetSystemFoldersNotification = SetSystemFoldersNotification.Archive; nSetSystemFoldersNotification = SetSystemFoldersNotification.Archive;
break; break;
// no default // no default
@ -394,8 +407,8 @@ class AppUser extends AbstractApp
if (sFromFolderFullNameRaw !== sToFolderFullNameRaw && isArray(aUidForMove) && 0 < aUidForMove.length) if (sFromFolderFullNameRaw !== sToFolderFullNameRaw && isArray(aUidForMove) && 0 < aUidForMove.length)
{ {
const const
oFromFolder = Cache.getFolderFromCacheList(sFromFolderFullNameRaw), oFromFolder = getFolderFromCacheList(sFromFolderFullNameRaw),
oToFolder = Cache.getFolderFromCacheList(sToFolderFullNameRaw); oToFolder = getFolderFromCacheList(sToFolderFullNameRaw);
if (oFromFolder && oToFolder) if (oFromFolder && oToFolder)
{ {
@ -649,14 +662,14 @@ class AppUser extends AbstractApp
check = false, check = false,
unreadCountChange = false; unreadCountChange = false;
const folderFromCache = Cache.getFolderFromCacheList(data.Result.Folder); const folderFromCache = getFolderFromCacheList(data.Result.Folder);
if (folderFromCache) if (folderFromCache)
{ {
folderFromCache.interval = Momentor.momentNowUnix(); folderFromCache.interval = momentNowUnix();
if (data.Result.Hash) if (data.Result.Hash)
{ {
Cache.setFolderHash(data.Result.Folder, data.Result.Hash); setFolderHash(data.Result.Folder, data.Result.Hash);
} }
if (isNormal(data.Result.MessageCount)) if (isNormal(data.Result.MessageCount))
@ -676,7 +689,7 @@ class AppUser extends AbstractApp
if (unreadCountChange) if (unreadCountChange)
{ {
Cache.clearMessageFlagsFromCacheByFolder(folderFromCache.fullNameRaw); clearMessageFlagsFromCacheByFolder(folderFromCache.fullNameRaw);
} }
if (data.Result.Flags) if (data.Result.Flags)
@ -687,7 +700,7 @@ class AppUser extends AbstractApp
{ {
check = true; check = true;
const flags = data.Result.Flags[uid]; 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 !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); 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 (data.Result.Hash !== hash || '' === hash || unreadCountChange)
{ {
if (folderFromCache.fullNameRaw === FolderStore.currentFolderFullNameRaw()) if (folderFromCache.fullNameRaw === FolderStore.currentFolderFullNameRaw())
{ {
this.reloadMessageList(); this.reloadMessageList();
} }
else if (Cache.getFolderInboxName() === folderFromCache.fullNameRaw) else if (getFolderInboxName() === folderFromCache.fullNameRaw)
{ {
this.recacheInboxMessageList(); this.recacheInboxMessageList();
} }
@ -733,12 +746,12 @@ class AppUser extends AbstractApp
{ {
if (oData && oData.Result && oData.Result.List && isNonEmptyArray(oData.Result.List)) if (oData && oData.Result && oData.Result.List && isNonEmptyArray(oData.Result.List))
{ {
const utc = Momentor.momentNowUnix(); const utc = momentNowUnix();
_.each(oData.Result.List, (oItem) => { _.each(oData.Result.List, (oItem) => {
const const
hash = Cache.getFolderHash(oItem.Folder), hash = getFolderHash(oItem.Folder),
folder = Cache.getFolderFromCacheList(oItem.Folder); folder = getFolderFromCacheList(oItem.Folder);
let let
unreadCountChange = false; unreadCountChange = false;
@ -748,7 +761,7 @@ class AppUser extends AbstractApp
if (oItem.Hash) if (oItem.Hash)
{ {
Cache.setFolderHash(oItem.Folder, oItem.Hash); setFolderHash(oItem.Folder, oItem.Hash);
} }
if (isNormal(oItem.MessageCount)) if (isNormal(oItem.MessageCount))
@ -768,7 +781,7 @@ class AppUser extends AbstractApp
if (unreadCountChange) if (unreadCountChange)
{ {
Cache.clearMessageFlagsFromCacheByFolder(folder.fullNameRaw); clearMessageFlagsFromCacheByFolder(folder.fullNameRaw);
} }
if (oItem.Hash !== hash || '' === hash) if (oItem.Hash !== hash || '' === hash)
@ -829,11 +842,10 @@ class AppUser extends AbstractApp
case MessageSetAction.SetSeen: case MessageSetAction.SetSeen:
_.each(rootUids, (sSubUid) => { _.each(rootUids, (sSubUid) => {
alreadyUnread += Cache.storeMessageFlagsToCacheBySetAction( alreadyUnread += storeMessageFlagsToCacheBySetAction(sFolderFullNameRaw, sSubUid, iSetAction);
sFolderFullNameRaw, sSubUid, iSetAction);
}); });
folder = Cache.getFolderFromCacheList(sFolderFullNameRaw); folder = getFolderFromCacheList(sFolderFullNameRaw);
if (folder) if (folder)
{ {
folder.messageCountUnread(folder.messageCountUnread() - alreadyUnread); folder.messageCountUnread(folder.messageCountUnread() - alreadyUnread);
@ -845,11 +857,11 @@ class AppUser extends AbstractApp
case MessageSetAction.UnsetSeen: case MessageSetAction.UnsetSeen:
_.each(rootUids, (sSubUid) => { _.each(rootUids, (sSubUid) => {
alreadyUnread += Cache.storeMessageFlagsToCacheBySetAction( alreadyUnread += storeMessageFlagsToCacheBySetAction(
sFolderFullNameRaw, sSubUid, iSetAction); sFolderFullNameRaw, sSubUid, iSetAction);
}); });
folder = Cache.getFolderFromCacheList(sFolderFullNameRaw); folder = getFolderFromCacheList(sFolderFullNameRaw);
if (folder) if (folder)
{ {
folder.messageCountUnread(folder.messageCountUnread() - alreadyUnread + rootUids.length); folder.messageCountUnread(folder.messageCountUnread() - alreadyUnread + rootUids.length);
@ -861,8 +873,7 @@ class AppUser extends AbstractApp
case MessageSetAction.SetFlag: case MessageSetAction.SetFlag:
_.each(rootUids, (sSubUid) => { _.each(rootUids, (sSubUid) => {
Cache.storeMessageFlagsToCacheBySetAction( storeMessageFlagsToCacheBySetAction(sFolderFullNameRaw, sSubUid, iSetAction);
sFolderFullNameRaw, sSubUid, iSetAction);
}); });
Remote.messageSetFlagged(noop, sFolderFullNameRaw, rootUids, true); Remote.messageSetFlagged(noop, sFolderFullNameRaw, rootUids, true);
@ -871,8 +882,7 @@ class AppUser extends AbstractApp
case MessageSetAction.UnsetFlag: case MessageSetAction.UnsetFlag:
_.each(rootUids, (sSubUid) => { _.each(rootUids, (sSubUid) => {
Cache.storeMessageFlagsToCacheBySetAction( storeMessageFlagsToCacheBySetAction(sFolderFullNameRaw, sSubUid, iSetAction);
sFolderFullNameRaw, sSubUid, iSetAction);
}); });
Remote.messageSetFlagged(noop, sFolderFullNameRaw, rootUids, false); Remote.messageSetFlagged(noop, sFolderFullNameRaw, rootUids, false);
@ -886,15 +896,15 @@ class AppUser extends AbstractApp
} }
googleConnect() { 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() { 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() { 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 LoginUserScreen
]); ]);
Plugins.runHook('rl-start-login-screens'); runHook('rl-start-login-screens');
Events.pub('rl.bootstart-login-screens'); Events.pub('rl.bootstart-login-screens');
} }
else else
{ {
routeOff(); routeOff();
setHash(Links.root(), true); setHash(root(), true);
routeOff(); routeOff();
_.defer(() => { _.defer(() => {
@ -1300,7 +1310,7 @@ class AppUser extends AbstractApp
if ('' !== startupUrl) if ('' !== startupUrl)
{ {
routeOff(); routeOff();
setHash(Links.root(startupUrl), true); setHash(root(startupUrl), true);
routeOn(); routeOn();
} }
@ -1314,7 +1324,7 @@ class AppUser extends AbstractApp
{ {
try try
{ {
PgpStore.openpgp.initWorker({path: Links.openPgpWorkerJs()}); PgpStore.openpgp.initWorker({path: openPgpWorkerJs()});
} }
catch (e) catch (e)
{ {
@ -1336,7 +1346,7 @@ class AppUser extends AbstractApp
} }
else else
{ {
jassl(Links.openPgpJs()).then(() => { jassl(openPgpJs()).then(() => {
if (window.openpgp) if (window.openpgp)
{ {
openpgpCallback(window.openpgp); openpgpCallback(window.openpgp);
@ -1360,10 +1370,10 @@ class AppUser extends AbstractApp
this.socialUsers(true); this.socialUsers(true);
} }
Events.sub('interval.2m', () => this.folderInformation(Cache.getFolderInboxName())); Events.sub('interval.2m', () => this.folderInformation(getFolderInboxName()));
Events.sub('interval.3m', () => { Events.sub('interval.3m', () => {
const sF = FolderStore.currentFolderFullNameRaw(); const sF = FolderStore.currentFolderFullNameRaw();
if (Cache.getFolderInboxName() !== sF) if (getFolderInboxName() !== sF)
{ {
this.folderInformation(sF); this.folderInformation(sF);
} }
@ -1385,7 +1395,7 @@ class AppUser extends AbstractApp
_.delay(() => { _.delay(() => {
const sF = FolderStore.currentFolderFullNameRaw(); const sF = FolderStore.currentFolderFullNameRaw();
if (Cache.getFolderInboxName() !== sF) if (getFolderInboxName() !== sF)
{ {
this.folderInformation(sF); this.folderInformation(sF);
} }
@ -1396,7 +1406,7 @@ class AppUser extends AbstractApp
Events.sub('rl.auto-logout', () => this.logout()); Events.sub('rl.auto-logout', () => this.logout());
Plugins.runHook('rl-start-user-screens'); runHook('rl-start-user-screens');
Events.pub('rl.bootstart-user-screens'); Events.pub('rl.bootstart-user-screens');
if (Settings.settingsGet('WelcomePageUrl')) 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'); Events.pub('rl.bootstart-end');
} }
} }

View file

@ -1,12 +1,315 @@
// import window from 'window'; import window from 'window';
import $ from '$'; import $ from '$';
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 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} * @returns {void}
@ -15,8 +318,10 @@ function init()
{ {
if (null === cmdDom) if (null === cmdDom)
{ {
cmdDom = $('<div class="rl-cmd"></div>'); cmdDom = $('<div></div>');
cmdDom.appendTo($body); cmdDom.appendTo($body);
bind(cmdDom);
} }
} }
@ -25,11 +330,21 @@ function init()
*/ */
export function toggle() export function toggle()
{ {
init(); if (Settings.appSettingsGet('allowCmdInterface'))
{
init();
opened = !opened; _.delay(() => {
if (contoller)
_.delay(() => { {
cmdDom.toggleClass('opened', opened); contoller.opened(!contoller.opened());
}, 50); if (contoller.opened())
{
_.delay(() => {
contoller.focused(true);
}, 50);
}
}
}, 50);
}
} }

View file

@ -312,7 +312,7 @@ export function reload(admin, language)
reloadData(); 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 $html
.removeClass('rl-changing-language') .removeClass('rl-changing-language')

View file

@ -1239,10 +1239,10 @@ let
/** /**
* @param {string} value * @param {string} value
* @param {function} themeTrigger * @param {function=} themeTrigger = noop
* @returns {void} * @returns {void}
*/ */
export function changeTheme(value, themeTrigger) export function changeTheme(value, themeTrigger = noop)
{ {
const const
themeLink = $('#app-theme-link'), themeLink = $('#app-theme-link'),
@ -1272,6 +1272,7 @@ export function changeTheme(value, themeTrigger)
} }
window.clearTimeout(__themeTimer); window.clearTimeout(__themeTimer);
themeTrigger(SaveSettingsStep.Animate); themeTrigger(SaveSettingsStep.Animate);
if (__themeAjax && __themeAjax.abort) if (__themeAjax && __themeAjax.abort)

21
dev/External/ko.js vendored
View file

@ -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 = { ko.bindingHandlers.onEnter = {
init: (element, fValueAccessor, fAllBindingsAccessor, viewModel) => { init: (element, fValueAccessor, fAllBindingsAccessor, viewModel) => {
$(element).on('keypress.koOnEnter', (event) => { $(element).on('keypress.koOnEnter', (event) => {
@ -431,7 +448,7 @@ ko.bindingHandlers.onTab = {
ko.bindingHandlers.onEsc = { ko.bindingHandlers.onEsc = {
init: (element, fValueAccessor, fAllBindingsAccessor, viewModel) => { init: (element, fValueAccessor, fAllBindingsAccessor, viewModel) => {
$(element).on('keypress.koOnEsc', (event) => { $(element).on('keyup.koOnEsc', (event) => {
if (event && 27 === window.parseInt(event.keyCode, 10)) if (event && 27 === window.parseInt(event.keyCode, 10))
{ {
$(element).trigger('change'); $(element).trigger('change');
@ -440,7 +457,7 @@ ko.bindingHandlers.onEsc = {
}); });
ko.utils.domNodeDisposal.addDisposeCallback(element, () => { ko.utils.domNodeDisposal.addDisposeCallback(element, () => {
$(element).off('keypress.koOnEsc'); $(element).off('keyup.koOnEsc');
}); });
} }
}; };

1
dev/Html/Cmds/Done.html Normal file
View file

@ -0,0 +1 @@
<span class="rl-cmd-clr-success">done!</span>

1
dev/Html/Cmds/Error.html Normal file
View file

@ -0,0 +1 @@
<span class="rl-cmd-clr-error">Command not found:</span> {{ cmd }}

1
dev/Html/Cmds/Help.html Normal file
View file

@ -0,0 +1 @@
<span class="rl-cmd-clr-info">commands:</span> {{ commands }}

View file

@ -0,0 +1 @@
lang <span class="rl-cmd-clr-info">[{{ langs }}]</span>

1
dev/Html/Cmds/Main.html Normal file
View file

@ -0,0 +1 @@
<span class="rl-cmd-clr-success">&gt;</span> {{ cmd }}

View file

@ -0,0 +1 @@
theme <span class="rl-cmd-clr-info">[{{ themes }}]</span>

View file

@ -0,0 +1 @@
<span class="rl-cmd-clr-info">version:</span> {{ version }}

View file

@ -7,12 +7,75 @@
top: auto; top: auto;
height: 0; height: 0;
background: rgba(0, 0, 0, .8); background: rgba(0, 0, 0, .85);
border-top: 1px solid #000; border-top: 1px solid #000;
overflow: hidden;
font-family: monospace;
.transition(height 0.1s ease-out); .transition(height 0.1s ease-out);
&.opened { &.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;
} }
} }

View file

@ -431,11 +431,11 @@ class LoginUserView extends AbstractViewNext
_.delay(() => { _.delay(() => {
LanguageStore.language.subscribe((sValue) => { LanguageStore.language.subscribe((value) => {
this.langRequest(true); this.langRequest(true);
translatorReload(false, sValue).then(() => { translatorReload(false, value).then(() => {
this.langRequest(false); this.langRequest(false);
this.bSendLanguage = true; this.bSendLanguage = true;
}, () => { }, () => {

View file

@ -491,10 +491,9 @@ gulp.task('rainloop:setup', ['rainloop:copy'], function() {
fs.writeFileSync(dist + 'data/EMPTY', versionFull); fs.writeFileSync(dist + 'data/EMPTY', versionFull);
fs.writeFileSync(dist + 'index.php', fs.readFileSync('index.php', 'utf8') fs.writeFileSync(dist + 'index.php', fs.readFileSync('index.php', 'utf8')
.replace('\'APP_VERSION\', \'0.0.0\'', '\'APP_VERSION\', \'' + versionFull + '\'')); .replace('\'APP_VERSION\', \'0.0.0\'', '\'APP_VERSION\', \'' + versionFull + '\'')
.replace('\'APP_VERSION_TYPE\', \'source\'', '\'APP_VERSION_TYPE\', \'' + (cfg.community ? 'community' : 'standard') + '\'')
fs.writeFileSync(dist + 'index.php', fs.readFileSync('index.php', 'utf8') );
.replace('\'APP_VERSION_TYPE\', \'source\'', '\'APP_VERSION\', \'' + (cfg.community ? 'community' : 'standard') + '\''));
fs.writeFileSync(dist + 'rainloop/v/' + versionFull + '/index.php.root', fs.readFileSync(dist + 'index.php')); fs.writeFileSync(dist + 'rainloop/v/' + versionFull + '/index.php.root', fs.readFileSync(dist + 'index.php'));

View file

@ -1441,6 +1441,7 @@ class Actions
return \array_merge(array( return \array_merge(array(
'version' => APP_VERSION, 'version' => APP_VERSION,
'admin' => $bAdmin,
'mobile' => $bMobile, 'mobile' => $bMobile,
'mobileDevice' => $bMobileDevice, 'mobileDevice' => $bMobileDevice,
'webPath' => \RainLoop\Utils::WebPath(), 'webPath' => \RainLoop\Utils::WebPath(),
@ -1461,11 +1462,13 @@ class Actions
'materialDesign' => (bool) $oConfig->Get('labs', 'use_material_design', true), 'materialDesign' => (bool) $oConfig->Get('labs', 'use_material_design', true),
'folderSpecLimit' => (int) $oConfig->Get('labs', 'folders_spec_limit', 50), 'folderSpecLimit' => (int) $oConfig->Get('labs', 'folders_spec_limit', 50),
'faviconStatus' => (bool) $oConfig->Get('labs', 'favicon_status', true), '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), 'useNativeScrollbars' => (bool) $oConfig->Get('interface', 'use_native_scrollbars', false),
'listPermanentFiltered' => '' !== \trim(\RainLoop\Api::Config()->Get('labs', 'imap_message_list_permanent_filter', '')), 'listPermanentFiltered' => '' !== \trim(\RainLoop\Api::Config()->Get('labs', 'imap_message_list_permanent_filter', '')),
'themes' => $this->GetThemes($bMobile, false), 'themes' => $this->GetThemes($bMobile, false),
'languages' => $this->GetLanguages(false), 'languages' => $this->GetLanguages(false),
'languagesAdmin' => $this->GetLanguages(true), 'languagesAdmin' => $this->GetLanguages(true),
'appVersionType' => APP_VERSION_TYPE,
'attachmentsActions' => $aAttachmentsActions 'attachmentsActions' => $aAttachmentsActions
), $bAdmin ? array( ), $bAdmin ? array(
'adminHostUse' => '' !== $oConfig->Get('security', 'admin_panel_host', ''), 'adminHostUse' => '' !== $oConfig->Get('security', 'admin_panel_host', ''),

View file

@ -446,6 +446,7 @@ Enables caching in the system'),
'startup_url' => array(''), 'startup_url' => array(''),
'nice_social_redirect' => array(true), 'nice_social_redirect' => array(true),
'strict_html_parser' => array(false), 'strict_html_parser' => array(false),
'allow_cmd' => array(false),
'dev_email' => array(''), 'dev_email' => array(''),
'dev_password' => array('') 'dev_password' => array('')
), ),

View 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>

View file

@ -1,26 +1,26 @@
<div class="popups"> <div class="popups">
<div class="modal hide b-ask-content g-ui-user-select-none" data-bind="modal: modalVisibility"> <div class="modal hide b-ask-content g-ui-user-select-none" data-bind="modal: modalVisibility">
<div> <div>
<div class="modal-body"> <div class="modal-body">
<br /> <br />
<br /> <br />
<span class="desc-place" data-bind="html: askDesc"></span> <span class="desc-place" data-bind="html: askDesc"></span>
<br /> <br />
<br /> <br />
<br /> <br />
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button class="btn buttonYes" data-bind="click: yesClick, hasFocus: yesFocus"> <button class="btn buttonYes" data-bind="click: yesClick, hasFocus: yesFocus">
<i class="icon-ok"></i> <i class="icon-ok"></i>
&nbsp;&nbsp; &nbsp;&nbsp;
<span data-bind="text: yesButton"></span> <span data-bind="text: yesButton"></span>
</button> </button>
<button class="btn buttonNo" data-bind="click: noClick, hasFocus: noFocus"> <button class="btn buttonNo" data-bind="click: noClick, hasFocus: noFocus">
<i class=" icon-remove"></i> <i class=" icon-remove"></i>
&nbsp;&nbsp; &nbsp;&nbsp;
<span data-bind="text: noButton"></span> <span data-bind="text: noButton"></span>
</button> </button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>

View file

@ -65,7 +65,7 @@ module.exports = function(publicPath, pro, es6) {
"transform-es2015-function-name", "transform-es2015-function-name",
// ["transform-es2015-arrow-functions")], // ["transform-es2015-arrow-functions")],
"transform-es2015-block-scoped-functions", "transform-es2015-block-scoped-functions",
// ["transform-es2015-classes", loose], // ["transform-es2015-classes", {loose: true}],
// "transform-es2015-object-super", // "transform-es2015-object-super",
"transform-es2015-shorthand-properties", "transform-es2015-shorthand-properties",
"transform-es2015-duplicate-keys", "transform-es2015-duplicate-keys",
@ -104,7 +104,7 @@ module.exports = function(publicPath, pro, es6) {
// other // other
'transform-runtime', 'transform-runtime',
'transform-decorators-legacy' 'transform-decorators-legacy' // -> transform-decorators // from stage-2
] ]
} }
}, },