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();
});
key('ctrl+shift+`', KeyState.All, () => {
toggleCmd();
});
if (Settings.appSettingsGet('allowCmdInterface'))
{
key('ctrl+shift+`', KeyState.All, () => {
toggleCmd();
});
}
}
remote() {

View file

@ -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(() => {

View file

@ -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');
}
}

View file

@ -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);
}
}

View file

@ -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')

View file

@ -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
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 = {
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
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;
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;
}
}

View file

@ -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;
}, () => {

View file

@ -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'));

View file

@ -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', ''),

View file

@ -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('')
),

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="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>
&nbsp;&nbsp;
<span data-bind="text: yesButton"></span>
</button>
<button class="btn buttonNo" data-bind="click: noClick, hasFocus: noFocus">
<i class=" icon-remove"></i>
&nbsp;&nbsp;
<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>
&nbsp;&nbsp;
<span data-bind="text: yesButton"></span>
</button>
<button class="btn buttonNo" data-bind="click: noClick, hasFocus: noFocus">
<i class=" icon-remove"></i>
&nbsp;&nbsp;
<span data-bind="text: noButton"></span>
</button>
</div>
</div>
</div>
</div>

View file

@ -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
]
}
},