mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-07-13 03:27:39 +03:00
Improve Squire toolbar
Cleanup some JS code
This commit is contained in:
parent
e52b9abc61
commit
0158a5fe1e
9 changed files with 163 additions and 200 deletions
|
|
@ -23,18 +23,13 @@ export function root(startupUrl = '') {
|
||||||
return HASH_PREFIX + pString(startupUrl);
|
return HASH_PREFIX + pString(startupUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
export function rootAdmin() {
|
|
||||||
return Settings.app('adminHostUse') ? ROOT : SERVER_PREFIX + (Settings.app('adminPath') || 'admin');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
export function logoutLink() {
|
export function logoutLink() {
|
||||||
return rl.adminArea() ? rootAdmin() : ROOT;
|
return (rl.adminArea() && !Settings.app('adminHostUse'))
|
||||||
|
? SERVER_PREFIX + (Settings.app('adminPath') || 'admin')
|
||||||
|
: ROOT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
.squire-toolbar select {
|
.squire-toolbar select {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
padding-left: 0;
|
padding: 4px 1.5em 4px 6px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
width: 7em;
|
width: 7em;
|
||||||
|
|
@ -18,6 +18,9 @@
|
||||||
.squire-toolbar select[data-action="fontSize"] {
|
.squire-toolbar select[data-action="fontSize"] {
|
||||||
width: 5em;
|
width: 5em;
|
||||||
}
|
}
|
||||||
|
.squire-toolbar button {
|
||||||
|
font-family: snappymail, var(--fontSans)
|
||||||
|
}
|
||||||
.squire-toolbar button[data-action="bold"] {
|
.squire-toolbar button[data-action="bold"] {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ import { decorateKoCommands, showScreenPopup } from 'Knoin/Knoin';
|
||||||
import { AbstractViewCenter } from 'Knoin/AbstractViews';
|
import { AbstractViewCenter } from 'Knoin/AbstractViews';
|
||||||
|
|
||||||
import { Settings } from 'Common/Globals';
|
import { Settings } from 'Common/Globals';
|
||||||
import { rootAdmin } from 'Common/Links';
|
|
||||||
|
|
||||||
import { LanguagesPopupView } from 'View/Popup/Languages';
|
import { LanguagesPopupView } from 'View/Popup/Languages';
|
||||||
|
|
||||||
|
|
@ -161,8 +160,6 @@ class LoginUserView extends AbstractViewCenter {
|
||||||
this.submitRequest(false);
|
this.submitRequest(false);
|
||||||
|
|
||||||
setTimeout(() => this.querySelector('.inputAdditionalCode').focus(), 100);
|
setTimeout(() => this.querySelector('.inputAdditionalCode').focus(), 100);
|
||||||
} else if (oData.Admin) {
|
|
||||||
setTimeout(() => location.href = rootAdmin(), 100);
|
|
||||||
} else {
|
} else {
|
||||||
rl.route.reload();
|
rl.route.reload();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,14 +64,12 @@
|
||||||
<span data-i18n="TOP_TOOLBAR/BUTTON_HELP"></span>
|
<span data-i18n="TOP_TOOLBAR/BUTTON_HELP"></span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="e-item dividerbar show-mobile" role="presentation">
|
<li class="e-item dividerbar" role="presentation">
|
||||||
<a class="e-link menuitem" href="#" tabindex="-1" data-bind="click: layoutDesktop">
|
<a class="e-link menuitem show-mobile" href="#" tabindex="-1" data-bind="click: layoutDesktop">
|
||||||
<i class="fontastic">💻</i>
|
<i class="fontastic">💻</i>
|
||||||
<span data-i18n="MOBILE/BUTTON_DESKTOP_VERSION"></span>
|
<span data-i18n="MOBILE/BUTTON_DESKTOP_VERSION"></span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
<a class="e-link menuitem hide-mobile" href="#" tabindex="-1" data-bind="click: layoutMobile">
|
||||||
<li class="e-item dividerbar hide-mobile" role="presentation">
|
|
||||||
<a class="e-link menuitem" href="#" tabindex="-1" data-bind="click: layoutMobile">
|
|
||||||
<i class="fontastic">📱</i>
|
<i class="fontastic">📱</i>
|
||||||
<span data-i18n="MOBILE/BUTTON_MOBILE_VERSION"></span>
|
<span data-i18n="MOBILE/BUTTON_MOBILE_VERSION"></span>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ config.paths.js = {
|
||||||
'dev/shortcuts.js',
|
'dev/shortcuts.js',
|
||||||
'vendors/routes/hasher.js',
|
'vendors/routes/hasher.js',
|
||||||
'vendors/routes/crossroads.js',
|
'vendors/routes/crossroads.js',
|
||||||
'vendors/jua/jua.min.js',
|
'vendors/jua/jua.js',
|
||||||
'vendors/qr.js/qr.min.js',
|
'vendors/qr.js/qr.min.js',
|
||||||
'vendors/bootstrap/js/bootstrap.native.js',
|
'vendors/bootstrap/js/bootstrap.native.js',
|
||||||
'vendors/knockout/build/output/knockout-latest.js',
|
'vendors/knockout/build/output/knockout-latest.js',
|
||||||
|
|
|
||||||
BIN
vendors/fontastic/fonts/snappymail.woff
vendored
BIN
vendors/fontastic/fonts/snappymail.woff
vendored
Binary file not shown.
BIN
vendors/fontastic/fonts/snappymail.woff2
vendored
BIN
vendors/fontastic/fonts/snappymail.woff2
vendored
Binary file not shown.
332
vendors/jua/jua.js
vendored
332
vendors/jua/jua.js
vendored
|
|
@ -82,173 +82,6 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @constructor
|
|
||||||
* @param {Jua} oJua
|
|
||||||
* @param {Object} oOptions
|
|
||||||
*/
|
|
||||||
class XHRDriver
|
|
||||||
{
|
|
||||||
constructor(oJua, oOptions)
|
|
||||||
{
|
|
||||||
this.oXhrs = {};
|
|
||||||
this.oUids = {};
|
|
||||||
this.oJua = oJua;
|
|
||||||
this.oOptions = Object.assign({
|
|
||||||
action: '',
|
|
||||||
name: 'juaFile',
|
|
||||||
hidden: {},
|
|
||||||
disableMultiple: false
|
|
||||||
}, oOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} sUid
|
|
||||||
*/
|
|
||||||
regTaskUid(sUid)
|
|
||||||
{
|
|
||||||
this.oUids[sUid] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} sUid
|
|
||||||
* @param {?} oFileInfo
|
|
||||||
*/
|
|
||||||
uploadTask(sUid, oFileInfo)
|
|
||||||
{
|
|
||||||
if (false === this.oUids[sUid] || !oFileInfo || !oFileInfo['File'])
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
const
|
|
||||||
self = this,
|
|
||||||
oXhr = new XMLHttpRequest(),
|
|
||||||
oFormData = new FormData(),
|
|
||||||
sAction = this.oOptions.action,
|
|
||||||
aHidden = this.oOptions.hidden,
|
|
||||||
fStartFunction = this.oJua.getEvent('onStart'),
|
|
||||||
fProgressFunction = this.oJua.getEvent('onProgress')
|
|
||||||
;
|
|
||||||
|
|
||||||
oXhr.open('POST', sAction, true);
|
|
||||||
|
|
||||||
if (fProgressFunction && oXhr.upload)
|
|
||||||
{
|
|
||||||
oXhr.upload.onprogress = oEvent => {
|
|
||||||
if (oEvent && oEvent.lengthComputable && defined(oEvent.loaded) && defined(oEvent.total))
|
|
||||||
{
|
|
||||||
fProgressFunction(sUid, oEvent.loaded, oEvent.total);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
oXhr.onreadystatechange = () => {
|
|
||||||
if (4 === oXhr.readyState)
|
|
||||||
{
|
|
||||||
delete self.oXhrs[sUid];
|
|
||||||
let bResult = false,
|
|
||||||
oResult = null;
|
|
||||||
if (200 === oXhr.status)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
oResult = JSON.parse(oXhr.responseText);
|
|
||||||
bResult = true;
|
|
||||||
}
|
|
||||||
catch (e)
|
|
||||||
{
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.oJua.getEvent('onComplete')(sUid, bResult, bResult ? oResult : null);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
fStartFunction && fStartFunction(sUid);
|
|
||||||
|
|
||||||
oFormData.append(this.oOptions.name, oFileInfo['File']);
|
|
||||||
Object.entries(aHidden).forEach(([key, value]) =>
|
|
||||||
oFormData.append(key, (typeof value === "function" ? value(oFileInfo) : value).toString())
|
|
||||||
);
|
|
||||||
|
|
||||||
oXhr.send(oFormData);
|
|
||||||
|
|
||||||
this.oXhrs[sUid] = oXhr;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (oError)
|
|
||||||
{
|
|
||||||
console.error(oError)
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
generateNewInput(oClickElement)
|
|
||||||
{
|
|
||||||
if (oClickElement)
|
|
||||||
{
|
|
||||||
const self = this,
|
|
||||||
oInput = doc.createElement('input'),
|
|
||||||
onClick = ()=>oInput.click();
|
|
||||||
|
|
||||||
oInput.type = 'file';
|
|
||||||
oInput.tabIndex = -1;
|
|
||||||
oInput.style.display = 'none';
|
|
||||||
oInput.multiple = !self.oOptions.disableMultiple;
|
|
||||||
|
|
||||||
oClickElement.addEventListener('click', onClick);
|
|
||||||
|
|
||||||
oInput.addEventListener('input', () => {
|
|
||||||
const fFileCallback = oFile => {
|
|
||||||
self.oJua.addNewFile(oFile);
|
|
||||||
setTimeout(() => {
|
|
||||||
oInput.remove();
|
|
||||||
oClickElement.removeEventListener('click', onClick);
|
|
||||||
self.generateNewInput(oClickElement);
|
|
||||||
}, 10);
|
|
||||||
};
|
|
||||||
if (oInput.files && oInput.files.length) {
|
|
||||||
getDataFromFiles(oInput.files, fFileCallback,
|
|
||||||
self.oOptions.multipleSizeLimit,
|
|
||||||
self.oJua.getEvent('onLimitReached')
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
fFileCallback({
|
|
||||||
'FileName': oInput.value.split('\\').pop().split('/').pop(),
|
|
||||||
'Size': null,
|
|
||||||
'Type': null,
|
|
||||||
'Folder': '',
|
|
||||||
'File' : null
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cancel(sUid)
|
|
||||||
{
|
|
||||||
this.oUids[sUid] = false;
|
|
||||||
if (this.oXhrs[sUid])
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
this.oXhrs[sUid].abort && this.oXhrs[sUid].abort();
|
|
||||||
}
|
|
||||||
catch (oError)
|
|
||||||
{
|
|
||||||
console.error(oError);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete this.oXhrs[sUid];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Queue extends Array
|
class Queue extends Array
|
||||||
{
|
{
|
||||||
constructor(limit) {
|
constructor(limit) {
|
||||||
|
|
@ -294,6 +127,10 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
oOptions = Object.assign({
|
oOptions = Object.assign({
|
||||||
|
action: '',
|
||||||
|
name: 'juaFile',
|
||||||
|
hidden: {},
|
||||||
|
disableMultiple: false,
|
||||||
queueSize: 10,
|
queueSize: 10,
|
||||||
clickElement: null,
|
clickElement: null,
|
||||||
dragAndDropElement: null,
|
dragAndDropElement: null,
|
||||||
|
|
@ -302,10 +139,11 @@
|
||||||
multipleSizeLimit: iDefLimit
|
multipleSizeLimit: iDefLimit
|
||||||
}, oOptions || {});
|
}, oOptions || {});
|
||||||
|
|
||||||
|
self.oXhrs = {};
|
||||||
|
self.oUids = {};
|
||||||
|
self.oOptions = oOptions;
|
||||||
self.oQueue = new Queue(oOptions.queueSize);
|
self.oQueue = new Queue(oOptions.queueSize);
|
||||||
|
|
||||||
self.oDriver = new XHRDriver(self, oOptions);
|
|
||||||
|
|
||||||
let el = oOptions.clickElement;
|
let el = oOptions.clickElement;
|
||||||
if (el) {
|
if (el) {
|
||||||
el.style.position = 'relative';
|
el.style.position = 'relative';
|
||||||
|
|
@ -314,7 +152,7 @@
|
||||||
el.style.display = 'inline-block';
|
el.style.display = 'inline-block';
|
||||||
}
|
}
|
||||||
|
|
||||||
self.oDriver.generateNewInput(el);
|
self.generateNewInput(el);
|
||||||
}
|
}
|
||||||
|
|
||||||
el = oOptions.dragAndDropElement;
|
el = oOptions.dragAndDropElement;
|
||||||
|
|
@ -458,14 +296,6 @@
|
||||||
return this.oEvents[sName] || null;
|
return this.oEvents[sName] || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} sUid
|
|
||||||
*/
|
|
||||||
cancel(sUid)
|
|
||||||
{
|
|
||||||
this.oDriver.cancel(sUid);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} oFileInfo
|
* @param {Object} oFileInfo
|
||||||
*/
|
*/
|
||||||
|
|
@ -483,12 +313,152 @@
|
||||||
const fOnSelect = this.getEvent('onSelect');
|
const fOnSelect = this.getEvent('onSelect');
|
||||||
if (oFileInfo && (!fOnSelect || (false !== fOnSelect(sUid, oFileInfo))))
|
if (oFileInfo && (!fOnSelect || (false !== fOnSelect(sUid, oFileInfo))))
|
||||||
{
|
{
|
||||||
this.oDriver.regTaskUid(sUid);
|
this.oUids[sUid] = true;
|
||||||
this.oQueue.push((...args) => this.oDriver.uploadTask(...args), sUid, oFileInfo);
|
this.oQueue.push((...args) => this.uploadTask(...args), sUid, oFileInfo);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this.oDriver.cancel(sUid);
|
this.cancel(sUid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} sUid
|
||||||
|
* @param {?} oFileInfo
|
||||||
|
*/
|
||||||
|
uploadTask(sUid, oFileInfo)
|
||||||
|
{
|
||||||
|
if (false === this.oUids[sUid] || !oFileInfo || !oFileInfo['File'])
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const
|
||||||
|
self = this,
|
||||||
|
oXhr = new XMLHttpRequest(),
|
||||||
|
oFormData = new FormData(),
|
||||||
|
sAction = this.oOptions.action,
|
||||||
|
aHidden = this.oOptions.hidden,
|
||||||
|
fStartFunction = this.getEvent('onStart'),
|
||||||
|
fProgressFunction = this.getEvent('onProgress')
|
||||||
|
;
|
||||||
|
|
||||||
|
oXhr.open('POST', sAction, true);
|
||||||
|
|
||||||
|
if (fProgressFunction && oXhr.upload)
|
||||||
|
{
|
||||||
|
oXhr.upload.onprogress = oEvent => {
|
||||||
|
if (oEvent && oEvent.lengthComputable && defined(oEvent.loaded) && defined(oEvent.total))
|
||||||
|
{
|
||||||
|
fProgressFunction(sUid, oEvent.loaded, oEvent.total);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
oXhr.onreadystatechange = () => {
|
||||||
|
if (4 === oXhr.readyState)
|
||||||
|
{
|
||||||
|
delete self.oXhrs[sUid];
|
||||||
|
let bResult = false,
|
||||||
|
oResult = null;
|
||||||
|
if (200 === oXhr.status)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
oResult = JSON.parse(oXhr.responseText);
|
||||||
|
bResult = true;
|
||||||
|
}
|
||||||
|
catch (e)
|
||||||
|
{
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.getEvent('onComplete')(sUid, bResult, bResult ? oResult : null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fStartFunction && fStartFunction(sUid);
|
||||||
|
|
||||||
|
oFormData.append(this.oOptions.name, oFileInfo['File']);
|
||||||
|
Object.entries(aHidden).forEach(([key, value]) =>
|
||||||
|
oFormData.append(key, (typeof value === "function" ? value(oFileInfo) : value).toString())
|
||||||
|
);
|
||||||
|
|
||||||
|
oXhr.send(oFormData);
|
||||||
|
|
||||||
|
this.oXhrs[sUid] = oXhr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (oError)
|
||||||
|
{
|
||||||
|
console.error(oError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
generateNewInput(oClickElement)
|
||||||
|
{
|
||||||
|
if (oClickElement)
|
||||||
|
{
|
||||||
|
const self = this,
|
||||||
|
oInput = doc.createElement('input'),
|
||||||
|
onClick = ()=>oInput.click();
|
||||||
|
|
||||||
|
oInput.type = 'file';
|
||||||
|
oInput.tabIndex = -1;
|
||||||
|
oInput.style.display = 'none';
|
||||||
|
oInput.multiple = !self.oOptions.disableMultiple;
|
||||||
|
|
||||||
|
oClickElement.addEventListener('click', onClick);
|
||||||
|
|
||||||
|
oInput.addEventListener('input', () => {
|
||||||
|
const fFileCallback = oFile => {
|
||||||
|
self.addNewFile(oFile);
|
||||||
|
setTimeout(() => {
|
||||||
|
oInput.remove();
|
||||||
|
oClickElement.removeEventListener('click', onClick);
|
||||||
|
self.generateNewInput(oClickElement);
|
||||||
|
}, 10);
|
||||||
|
};
|
||||||
|
if (oInput.files && oInput.files.length) {
|
||||||
|
getDataFromFiles(oInput.files, fFileCallback,
|
||||||
|
self.oOptions.multipleSizeLimit,
|
||||||
|
self.getEvent('onLimitReached')
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
fFileCallback({
|
||||||
|
'FileName': oInput.value.split('\\').pop().split('/').pop(),
|
||||||
|
'Size': null,
|
||||||
|
'Type': null,
|
||||||
|
'Folder': '',
|
||||||
|
'File' : null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} sUid
|
||||||
|
*/
|
||||||
|
cancel(sUid)
|
||||||
|
{
|
||||||
|
this.oUids[sUid] = false;
|
||||||
|
if (this.oXhrs[sUid])
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
this.oXhrs[sUid].abort && this.oXhrs[sUid].abort();
|
||||||
|
}
|
||||||
|
catch (oError)
|
||||||
|
{
|
||||||
|
console.error(oError);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete this.oXhrs[sUid];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2
vendors/jua/jua.min.js
vendored
2
vendors/jua/jua.min.js
vendored
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue