mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-06-28 14:55:48 +00:00
Use less jQuery, more native
This commit is contained in:
24
README.md
24
README.md
@@ -81,29 +81,29 @@ Things might work in Edge 18, Firefox 50-62 and Chrome 54-68 due to one polyfill
|
||||
|
||||
|js/* |1.14.0 |native |
|
||||
|----------- |--------: |--------: |
|
||||
|admin.js |2.130.942 |1.007.370 |
|
||||
|app.js |4.184.455 |2.676.306 |
|
||||
|boot.js | 671.522 | 43.856 |
|
||||
|libs.js | 647.614 | 316.969 |
|
||||
|admin.js |2.130.942 | 973.239 |
|
||||
|app.js |4.184.455 |2.640.205 |
|
||||
|boot.js | 671.522 | 43.824 |
|
||||
|libs.js | 647.614 | 316.970 |
|
||||
|polyfills.js | 325.834 | 0 |
|
||||
|TOTAL |7.960.367 |4.044.501 |
|
||||
|TOTAL |7.960.367 |3.974.238 |
|
||||
|
||||
|js/min/* |1.14.0 |native |gzip 1.14 |gzip |brotli |
|
||||
|--------------- |--------: |--------: |--------: |--------: |--------: |
|
||||
|admin.min.js | 252.147 | 138.101 | 73.657 | 40.104 | 34.210 |
|
||||
|app.min.js | 511.202 | 360.198 |140.462 | 95.043 | 76.240 |
|
||||
|boot.min.js | 66.007 | 5.575 | 22.567 | 2.340 | 2.000 |
|
||||
|admin.min.js | 252.147 | 132.156 | 73.657 | 38.184 | 32.740 |
|
||||
|app.min.js | 511.202 | 356.109 |140.462 | 93.738 | 75.150 |
|
||||
|boot.min.js | 66.007 | 5.560 | 22.567 | 2.341 | 2.004 |
|
||||
|libs.min.js | 572.545 | 300.691 |176.720 | 92.925 | 82.046 |
|
||||
|polyfills.min.js | 32.452 | 0 | 11.312 | 0 | 0 |
|
||||
|TOTAL |1.434.353 | 804.565 |424.718 |230.412 |194.496 |
|
||||
|TOTAL |1.434.353 | 794.516 |424.718 |227.188 |191.940 |
|
||||
|
||||
629.788 bytes (194.306 gzip) is not much, but it feels faster.
|
||||
639.837 bytes (197.530 gzip) is not much, but it feels faster.
|
||||
|
||||
|
||||
|css/* |1.14.0 |native |
|
||||
|-------------- |--------: |--------: |
|
||||
|app.css | 340.334 | 266.769 |
|
||||
|app.min.css | 274.791 | 211.601 |
|
||||
|app.css | 340.334 | 266.739 |
|
||||
|app.min.css | 274.791 | 211.574 |
|
||||
|
||||
|
||||
### PHP73 branch
|
||||
|
||||
@@ -72,7 +72,7 @@ class AbstractApp extends AbstractBoot {
|
||||
} else {
|
||||
const oLink = document.createElement('a');
|
||||
oLink.href = link;
|
||||
document.body.appendChild(oLink).click();
|
||||
document.body.append(oLink).click();
|
||||
oLink.remove();
|
||||
// open(link, '_self');
|
||||
}
|
||||
@@ -165,11 +165,7 @@ class AbstractApp extends AbstractBoot {
|
||||
ko.components.register('SaveTrigger', require('Component/SaveTrigger').default);
|
||||
ko.components.register('Input', require('Component/Input').default);
|
||||
ko.components.register('Select', require('Component/Select').default);
|
||||
ko.components.register('Radio', require('Component/Radio').default);
|
||||
ko.components.register('TextArea', require('Component/TextArea').default);
|
||||
ko.components.register('Date', require('Component/Date').default);
|
||||
|
||||
ko.components.register('x-script', require('Component/Script').default);
|
||||
|
||||
if (Settings.appSettingsGet('materialDesign') && !bMobileDevice) {
|
||||
ko.components.register('Checkbox', require('Component/MaterialDesign/Checkbox').default);
|
||||
|
||||
@@ -819,7 +819,7 @@ class AppUser extends AbstractApp {
|
||||
const resizer = doc.createElement('div'),
|
||||
cssint = s => parseFloat(getComputedStyle(source, null).getPropertyValue(s).replace('px', ''));
|
||||
resizer.className = 'resizer';
|
||||
source.appendChild(resizer);
|
||||
source.append(resizer);
|
||||
resizer.addEventListener('mousedown', {
|
||||
source: source,
|
||||
mode: mode,
|
||||
@@ -995,7 +995,7 @@ class AppUser extends AbstractApp {
|
||||
script.onload = openpgpCallback;
|
||||
script.onerror = () => console.error(script.src);
|
||||
script.src = openPgpJs();
|
||||
doc.head.appendChild(script);
|
||||
doc.head.append(script);
|
||||
}
|
||||
} else {
|
||||
PgpStore.capaOpenPGP(false);
|
||||
|
||||
@@ -45,8 +45,8 @@ function writeCSS(css) {
|
||||
const style = doc.createElement('style');
|
||||
style.type = 'text/css';
|
||||
style.textContent = css;
|
||||
// style.appendChild(doc.createTextNode(styles));
|
||||
doc.head.appendChild(style);
|
||||
// style.append(doc.createTextNode(styles));
|
||||
doc.head.append(style);
|
||||
}
|
||||
|
||||
function loadScript(src) {
|
||||
@@ -59,8 +59,8 @@ function loadScript(src) {
|
||||
script.onerror = () => reject(new Error(src));
|
||||
script.src = src;
|
||||
// script.type = 'text/javascript';
|
||||
doc.head.appendChild(script);
|
||||
// doc.body.appendChild(element);
|
||||
doc.head.append(script);
|
||||
// doc.body.append(element);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import ko from 'ko';
|
||||
import { KeyState } from 'Common/Enums';
|
||||
|
||||
export const $html = jQuery('html');
|
||||
export const $htmlCL = document.documentElement.classList;
|
||||
export const $html = document.documentElement;
|
||||
export const $htmlCL = $html.classList;
|
||||
|
||||
/**
|
||||
* @type {?}
|
||||
|
||||
@@ -106,7 +106,6 @@ class HtmlEditor {
|
||||
this.onModeChange = onModeChange;
|
||||
|
||||
this.element = element;
|
||||
this.$element = jQuery(element);
|
||||
|
||||
this.resize = this.resizeEditor.throttle(100);
|
||||
|
||||
@@ -414,7 +413,7 @@ class HtmlEditor {
|
||||
resizeEditor() {
|
||||
if (this.editor && this.__resizable) {
|
||||
try {
|
||||
this.editor.resize(this.$element.width(), this.$element.innerHeight());
|
||||
this.editor.resize(this.element.clientWidth, this.element.clientHeight);
|
||||
} catch (e) {} // eslint-disable-line no-empty
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ class Selector {
|
||||
|
||||
iSelectNextHelper = 0;
|
||||
iFocusedNextHelper = 0;
|
||||
oContentVisible;
|
||||
oContentScrollable;
|
||||
|
||||
sItemSelector;
|
||||
@@ -262,12 +261,11 @@ class Selector {
|
||||
this.focusedItem(null);
|
||||
}
|
||||
|
||||
init(contentVisible, contentScrollable, keyScope = 'all') {
|
||||
this.oContentVisible = contentVisible;
|
||||
this.oContentScrollable = contentScrollable ? contentScrollable[0] : null;
|
||||
init(contentScrollable, keyScope = 'all') {
|
||||
this.oContentScrollable = contentScrollable;
|
||||
|
||||
if (this.oContentVisible && this.oContentScrollable) {
|
||||
jQuery(this.oContentVisible)
|
||||
if (contentScrollable) {
|
||||
jQuery(contentScrollable)
|
||||
.on('selectstart', (event) => {
|
||||
if (event && event.preventDefault) {
|
||||
event.preventDefault();
|
||||
@@ -289,8 +287,9 @@ class Selector {
|
||||
});
|
||||
|
||||
key('enter', keyScope, () => {
|
||||
if (this.focusedItem() && !this.focusedItem().selected()) {
|
||||
this.actionClick(this.focusedItem());
|
||||
const focused = this.focusedItem();
|
||||
if (focused && !focused.selected()) {
|
||||
this.actionClick(focused);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -500,45 +499,29 @@ class Selector {
|
||||
* @returns {boolean}
|
||||
*/
|
||||
scrollToFocused() {
|
||||
if (!this.oContentVisible || !this.oContentScrollable) {
|
||||
return false;
|
||||
const scrollable = this.oContentScrollable;
|
||||
if (scrollable) {
|
||||
let block, focused = scrollable.querySelector(this.sItemFocusedSelector);
|
||||
if (focused) {
|
||||
const fRect = focused.getBoundingClientRect(),
|
||||
sRect = scrollable.getBoundingClientRect();
|
||||
if (fRect.top < sRect.top) {
|
||||
block = 'start';
|
||||
} else if (fRect.bottom > sRect.bottom) {
|
||||
block = 'end';
|
||||
}
|
||||
block && focused.scrollIntoView(block === 'start');
|
||||
} else {
|
||||
scrollable.scrollTop = 0;
|
||||
}
|
||||
}
|
||||
|
||||
const offset = 20,
|
||||
list = this.list(),
|
||||
$focused = jQuery(this.sItemFocusedSelector, this.oContentScrollable),
|
||||
pos = $focused.position(),
|
||||
visibleHeight = this.oContentVisible.height(),
|
||||
focusedHeight = $focused.outerHeight();
|
||||
|
||||
if (list && list[0] && list[0].focused()) {
|
||||
this.oContentScrollable.scrollTop = 0;
|
||||
return true;
|
||||
} else if (pos && (0 > pos.top || pos.top + focusedHeight > visibleHeight)) {
|
||||
let top = this.oContentScrollable.scrollTop + pos.top;
|
||||
this.oContentScrollable.scrollTop =
|
||||
0 > pos.top
|
||||
? top - offset
|
||||
: top - visibleHeight + focusedHeight + offset
|
||||
;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {boolean}
|
||||
*/
|
||||
scrollToTop() {
|
||||
if (!this.oContentVisible || !this.oContentScrollable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.oContentScrollable.scrollTop = 0;
|
||||
|
||||
return true;
|
||||
this.oContentScrollable && (this.oContentScrollable.scrollTop = 0);
|
||||
}
|
||||
|
||||
eventClickFunction(item, event) {
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import ko from 'ko';
|
||||
import { Notification, UploadErrorCode } from 'Common/Enums';
|
||||
import { pInt } from 'Common/Utils';
|
||||
import { $html, $htmlCL } from 'Common/Globals';
|
||||
import { langLink } from 'Common/Links';
|
||||
|
||||
let I18N_DATA = window.rainloopI18N || {};
|
||||
|
||||
const I18N_NOTIFICATION_DATA = {};
|
||||
const I18N_NOTIFICATION_MAP = [
|
||||
const doc = document,
|
||||
I18N_NOTIFICATION_DATA = {},
|
||||
I18N_NOTIFICATION_MAP = [
|
||||
[Notification.InvalidToken, 'NOTIFICATIONS/INVALID_TOKEN'],
|
||||
[Notification.InvalidToken, 'NOTIFICATIONS/INVALID_TOKEN'],
|
||||
[Notification.AuthError, 'NOTIFICATIONS/AUTH_ERROR'],
|
||||
@@ -101,26 +100,24 @@ export function i18n(key, valueList, defaulValue) {
|
||||
return result;
|
||||
}
|
||||
|
||||
const i18nToNode = (element) => {
|
||||
const $el = jQuery(element),
|
||||
key = $el.data('i18n');
|
||||
|
||||
const i18nToNode = element => {
|
||||
const key = element.dataset.i18n;
|
||||
if (key) {
|
||||
if ('[' === key.substr(0, 1)) {
|
||||
switch (key.substr(0, 6)) {
|
||||
case '[html]':
|
||||
$el.html(i18n(key.substr(6)));
|
||||
element.innerHTML = i18n(key.substr(6));
|
||||
break;
|
||||
case '[place':
|
||||
$el.attr('placeholder', i18n(key.substr(13)));
|
||||
element.placeholder = i18n(key.substr(13));
|
||||
break;
|
||||
case '[title':
|
||||
$el.attr('title', i18n(key.substr(7)));
|
||||
element.title = i18n(key.substr(7));
|
||||
break;
|
||||
// no default
|
||||
}
|
||||
} else {
|
||||
$el.text(i18n(key));
|
||||
element.textContent = i18n(key);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -129,11 +126,9 @@ const i18nToNode = (element) => {
|
||||
* @param {Object} elements
|
||||
* @param {boolean=} animate = false
|
||||
*/
|
||||
export function i18nToNodes(elements) {
|
||||
export function i18nToNodes(element) {
|
||||
setTimeout(() =>
|
||||
jQuery('[data-i18n]', elements).each((index, item) => {
|
||||
i18nToNode(item);
|
||||
})
|
||||
element.querySelectorAll('[data-i18n]').forEach(item => i18nToNode(item))
|
||||
, 1);
|
||||
}
|
||||
|
||||
@@ -141,7 +136,7 @@ const reloadData = () => {
|
||||
if (window.rainloopI18N) {
|
||||
I18N_DATA = window.rainloopI18N || {};
|
||||
|
||||
i18nToNodes(document);
|
||||
i18nToNodes(doc);
|
||||
|
||||
dispatchEvent(new CustomEvent('reload-time'));
|
||||
trigger(!trigger());
|
||||
@@ -209,7 +204,7 @@ export function getNotification(code, message = '', defCode = null) {
|
||||
*/
|
||||
export function getNotificationFromResponse(response, defCode = Notification.UnknownNotification) {
|
||||
return response && response.ErrorCode
|
||||
? getNotification(pInt(response.ErrorCode), response.ErrorMessage || '')
|
||||
? getNotification(parseInt(response.ErrorCode, 10) || defCode, response.ErrorMessage || '')
|
||||
: getNotification(defCode);
|
||||
}
|
||||
|
||||
@@ -253,8 +248,6 @@ export function getUploadErrorDescByCode(code) {
|
||||
export function reload(admin, language) {
|
||||
const start = Date.now();
|
||||
|
||||
$htmlCL.add('rl-changing-language');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
return fetch(langLink(language, admin), {cache: 'reload'})
|
||||
.then(response => {
|
||||
@@ -269,18 +262,19 @@ export function reload(admin, language) {
|
||||
reject(new Error(error.message))
|
||||
})
|
||||
.then(data => {
|
||||
var script = document.createElement('script');
|
||||
var script = doc.createElement('script');
|
||||
script.text = data;
|
||||
document.head.appendChild(script).parentNode.removeChild(script);
|
||||
doc.head.append(script).remove();
|
||||
setTimeout(
|
||||
() => {
|
||||
reloadData();
|
||||
|
||||
const isRtl = ['ar', 'ar_sa', 'he', 'he_he', 'ur', 'ur_ir'].includes((language || '').toLowerCase());
|
||||
const isRtl = ['ar', 'ar_sa', 'he', 'he_he', 'ur', 'ur_ir'].includes((language || '').toLowerCase()),
|
||||
htmlCL = doc.documentElement.classList;
|
||||
|
||||
$htmlCL.remove('rl-changing-language', 'rl-rtl', 'rl-ltr');
|
||||
// $html.attr('dir', isRtl ? 'rtl' : 'ltr')
|
||||
$htmlCL.add(isRtl ? 'rl-rtl' : 'rl-ltr');
|
||||
htmlCL.remove('rl-rtl', 'rl-ltr');
|
||||
htmlCL.add(isRtl ? 'rl-rtl' : 'rl-ltr');
|
||||
// doc.documentElement.dir = isRtl ? 'rtl' : 'ltr'
|
||||
|
||||
resolve();
|
||||
},
|
||||
@@ -289,6 +283,3 @@ export function reload(admin, language) {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// init section
|
||||
$htmlCL.add('rl-' + ($html.attr('dir') || 'ltr'));
|
||||
|
||||
@@ -3,8 +3,7 @@ import { Mime } from 'Common/Mime';
|
||||
|
||||
const
|
||||
doc = document,
|
||||
$ = jQuery,
|
||||
$div = $('<div></div>'),
|
||||
tpl = doc.createElement('template'),
|
||||
isArray = Array.isArray,
|
||||
htmlmap = {
|
||||
'&': '&',
|
||||
@@ -15,6 +14,13 @@ const
|
||||
},
|
||||
htmlspecialchars = str => (''+str).replace(/[&<>"']/g, m => htmlmap[m]);
|
||||
|
||||
export function htmlToElement(html) {
|
||||
var template = document.createElement('template');
|
||||
template.innerHTML = html.trim();
|
||||
return template.content.firstChild;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {(string|number)} value
|
||||
* @param {boolean=} includeZero = true
|
||||
@@ -137,7 +143,7 @@ export function inFocus() {
|
||||
try {
|
||||
if (doc.activeElement) {
|
||||
if (undefined === doc.activeElement.__inFocusCache) {
|
||||
doc.activeElement.__inFocusCache = $(doc.activeElement).is(
|
||||
doc.activeElement.__inFocusCache = doc.activeElement.matches(
|
||||
'input,textarea,iframe,.cke_editable'
|
||||
);
|
||||
}
|
||||
@@ -156,8 +162,7 @@ export function inFocus() {
|
||||
export function removeInFocus(force) {
|
||||
if (doc.activeElement && doc.activeElement.blur) {
|
||||
try {
|
||||
const activeEl = $(doc.activeElement);
|
||||
if (force || (activeEl && activeEl.is('input,textarea'))) {
|
||||
if (force || doc.activeElement.matches('input,textarea')) {
|
||||
doc.activeElement.blur();
|
||||
}
|
||||
} catch (e) {} // eslint-disable-line no-empty
|
||||
@@ -275,19 +280,6 @@ export function convertLangName(language, isEng = false) {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {object}
|
||||
*/
|
||||
export function draggablePlace() {
|
||||
return $(
|
||||
'<div class="draggablePlace">' +
|
||||
'<span class="text"></span> ' +
|
||||
'<i class="icon-copy icon-white visible-on-ctrl"></i>' +
|
||||
'<i class="icon-mail icon-white hidden-on-ctrl"></i>' +
|
||||
'</div>'
|
||||
).appendTo('#rl-hidden');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} domOption
|
||||
* @param {object} item
|
||||
@@ -299,65 +291,6 @@ export function defautOptionsAfterRender(domItem, item) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} title
|
||||
* @param {Object} body
|
||||
* @param {boolean} isHtml
|
||||
* @param {boolean} print
|
||||
*/
|
||||
export function clearBqSwitcher(body) {
|
||||
body.find('blockquote.rl-bq-switcher').removeClass('rl-bq-switcher hidden-bq');
|
||||
body
|
||||
.find('.rlBlockquoteSwitcher')
|
||||
.off('.rlBlockquoteSwitcher')
|
||||
.remove();
|
||||
body.find('[data-html-editor-font-wrapper]').removeAttr('data-html-editor-font-wrapper');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} messageData
|
||||
* @param {Object} body
|
||||
* @param {boolean} isHtml
|
||||
* @param {boolean} print
|
||||
* @returns {void}
|
||||
*/
|
||||
export function previewMessage(
|
||||
{ title, subject, date, fromCreds, toCreds, toLabel, ccClass, ccCreds, ccLabel },
|
||||
body,
|
||||
isHtml,
|
||||
print
|
||||
) {
|
||||
const win = open(''),
|
||||
doc = win.document,
|
||||
bodyClone = body.clone(),
|
||||
bodyClass = isHtml ? 'html' : 'plain';
|
||||
|
||||
clearBqSwitcher(bodyClone);
|
||||
|
||||
const html = bodyClone ? bodyClone.html() : '';
|
||||
|
||||
doc.write(
|
||||
deModule(require('Html/PreviewMessage.html'))
|
||||
.replace('{{title}}', encodeHtml(title))
|
||||
.replace('{{subject}}', encodeHtml(subject))
|
||||
.replace('{{date}}', encodeHtml(date))
|
||||
.replace('{{fromCreds}}', encodeHtml(fromCreds))
|
||||
.replace('{{toCreds}}', encodeHtml(toCreds))
|
||||
.replace('{{toLabel}}', encodeHtml(toLabel))
|
||||
.replace('{{ccClass}}', encodeHtml(ccClass))
|
||||
.replace('{{ccCreds}}', encodeHtml(ccCreds))
|
||||
.replace('{{ccLabel}}', encodeHtml(ccLabel))
|
||||
.replace('{{bodyClass}}', bodyClass)
|
||||
.replace('{{html}}', html)
|
||||
);
|
||||
|
||||
doc.close();
|
||||
|
||||
if (print) {
|
||||
setTimeout(() => win.print(), 100);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Function} fCallback
|
||||
* @param {?} koTrigger
|
||||
@@ -483,7 +416,7 @@ export function htmlToPlain(html) {
|
||||
fixAttibuteValue = (...args) => (args && 1 < args.length ? '' + args[1] + htmlspecialchars(args[2]) : ''),
|
||||
convertLinks = (...args) => (args && 1 < args.length ? args[1].trim() : '');
|
||||
|
||||
text = html
|
||||
tpl.innerHTML = html
|
||||
.replace(/<p[^>]*><\/p>/gi, '')
|
||||
.replace(/<pre[^>]*>([\s\S\r\n\t]*)<\/pre>/gim, convertPre)
|
||||
.replace(/[\s]+/gm, ' ')
|
||||
@@ -507,16 +440,13 @@ export function htmlToPlain(html) {
|
||||
.replace(/"/gi, '"')
|
||||
.replace(/<[^>]*>/gm, '');
|
||||
|
||||
text = $div.html(text).text();
|
||||
|
||||
text = text
|
||||
text = splitPlainText(tpl.textContent
|
||||
.replace(/\n[ \t]+/gm, '\n')
|
||||
.replace(/[\n]{3,}/gm, '\n\n')
|
||||
.replace(/>/gi, '>')
|
||||
.replace(/</gi, '<')
|
||||
.replace(/&/gi, '&');
|
||||
|
||||
text = splitPlainText(text);
|
||||
.replace(/&/gi, '&')
|
||||
);
|
||||
|
||||
pos = 0;
|
||||
limit = 800;
|
||||
@@ -530,7 +460,6 @@ export function htmlToPlain(html) {
|
||||
|
||||
if ((-1 === iP2 || iP3 < iP2) && iP1 < iP3) {
|
||||
text = text.substring(0, iP1) + convertBlockquote(text.substring(iP1 + 13, iP3)) + text.substring(iP3 + 11);
|
||||
|
||||
pos = 0;
|
||||
} else if (-1 < iP2 && iP2 < iP3) {
|
||||
pos = iP2 - 1;
|
||||
@@ -552,7 +481,7 @@ export function htmlToPlain(html) {
|
||||
* @param {boolean} findEmailAndLinksInText = false
|
||||
* @returns {string}
|
||||
*/
|
||||
export function plainToHtml(plain, findEmailAndLinksInText = false) {
|
||||
export function plainToHtml(plain) {
|
||||
plain = plain.toString().replace(/\r/g, '');
|
||||
plain = plain.replace(/^>[> ]>+/gm, ([match]) => (match ? match.replace(/[ ]+/g, '') : match));
|
||||
|
||||
@@ -595,9 +524,7 @@ export function plainToHtml(plain, findEmailAndLinksInText = false) {
|
||||
aText = aNextText;
|
||||
} while (bDo);
|
||||
|
||||
plain = aText.join('\n');
|
||||
|
||||
plain = plain
|
||||
return aText.join('\n')
|
||||
// .replace(/~~~\/blockquote~~~\n~~~blockquote~~~/g, '\n')
|
||||
.replace(/&/g, '&')
|
||||
.replace(/>/g, '>')
|
||||
@@ -605,8 +532,6 @@ export function plainToHtml(plain, findEmailAndLinksInText = false) {
|
||||
.replace(/~~~blockquote~~~[\s]*/g, '<blockquote>')
|
||||
.replace(/[\s]*~~~\/blockquote~~~/g, '</blockquote>')
|
||||
.replace(/\n/g, '<br />');
|
||||
|
||||
return findEmailAndLinksInText ? findEmailAndLinks(plain) : plain;
|
||||
}
|
||||
|
||||
window['rainloop_Utils_htmlToPlain'] = htmlToPlain; // eslint-disable-line dot-notation
|
||||
@@ -766,39 +691,6 @@ export function selectElement(element) {
|
||||
sel.addRange(range);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {boolean=} delay = false
|
||||
*/
|
||||
export function triggerAutocompleteInputChange(delay = false) {
|
||||
const fFunc = () => $('.checkAutocomplete').trigger('change');
|
||||
|
||||
if (delay) {
|
||||
setTimeout(fFunc, 100);
|
||||
} else {
|
||||
fFunc();
|
||||
}
|
||||
}
|
||||
|
||||
const configurationScriptTagCache = {};
|
||||
|
||||
/**
|
||||
* @param {string} configuration
|
||||
* @returns {object}
|
||||
*/
|
||||
export function getConfigurationFromScriptTag(configuration) {
|
||||
if (!configurationScriptTagCache[configuration]) {
|
||||
configurationScriptTagCache[configuration] = $(
|
||||
'script[type="application/json"][data-configuration="' + configuration + '"]'
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(configurationScriptTagCache[configuration].text());
|
||||
} catch (e) {} // eslint-disable-line no-empty
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object|Array} objectOrObjects
|
||||
* @returns {void}
|
||||
|
||||
@@ -30,7 +30,7 @@ const componentExportHelper = (ClassObject, templateID = '') => ({
|
||||
params.component = componentInfo;
|
||||
params.element = jQuery(componentInfo.element);
|
||||
|
||||
i18nToNodes(params.element);
|
||||
i18nToNodes(componentInfo.element);
|
||||
|
||||
if (undefined !== params.inline && ko.unwrap(params.inline)) {
|
||||
params.element.css('display', 'inline-block');
|
||||
|
||||
19
dev/External/ko.js
vendored
19
dev/External/ko.js
vendored
@@ -2,6 +2,7 @@ import { SaveSettingsStep } from 'Common/Enums';
|
||||
|
||||
const
|
||||
$ = jQuery,
|
||||
doc = document,
|
||||
ko = window.ko,
|
||||
Translator = () => require('Common/Translator'),
|
||||
isFunction = v => typeof v === 'function';
|
||||
@@ -75,7 +76,7 @@ ko.bindingHandlers.tooltip = {
|
||||
|
||||
ko.bindingHandlers.tooltipErrorTip = {
|
||||
init: element => {
|
||||
document.addEventListener('click', () => element.removeAttribute('data-rainloopErrorTip'));
|
||||
doc.addEventListener('click', () => element.removeAttribute('data-rainloopErrorTip'));
|
||||
ko.utils.domNodeDisposal.addDisposeCallback(element, () => element.removeAttribute('data-rainloopErrorTip'));
|
||||
},
|
||||
update: (element, fValueAccessor) => {
|
||||
@@ -197,13 +198,13 @@ ko.bindingHandlers.modal = {
|
||||
});
|
||||
},
|
||||
update: (element, fValueAccessor) => {
|
||||
const Globals = require('Common/Globals');
|
||||
const htmlCL = doc.documentElement.classList;
|
||||
|
||||
$(element).modal(ko.unwrap(fValueAccessor()) ? 'show' : 'hide');
|
||||
|
||||
if (Globals.$htmlCL.contains('no-mobile')) {
|
||||
Globals.$htmlCL.add('rl-modal-animation');
|
||||
setTimeout(() => Globals.$htmlCL.remove('rl-modal-animation'), 500);
|
||||
if (htmlCL.contains('no-mobile')) {
|
||||
htmlCL.add('rl-modal-animation');
|
||||
setTimeout(() => htmlCL.remove('rl-modal-animation'), 500);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -226,11 +227,11 @@ ko.bindingHandlers.i18nUpdate = {
|
||||
};
|
||||
|
||||
ko.bindingHandlers.link = {
|
||||
update: (element, fValueAccessor) => element.setAttribute('href', ko.unwrap(fValueAccessor()))
|
||||
update: (element, fValueAccessor) => element.href = ko.unwrap(fValueAccessor())
|
||||
};
|
||||
|
||||
ko.bindingHandlers.title = {
|
||||
update: (element, fValueAccessor) => element.setAttribute('title', ko.unwrap(fValueAccessor()))
|
||||
update: (element, fValueAccessor) => element.title = ko.unwrap(fValueAccessor())
|
||||
};
|
||||
|
||||
ko.bindingHandlers.initDom = {
|
||||
@@ -247,7 +248,7 @@ ko.bindingHandlers.draggable = {
|
||||
scrollSpeed = 3,
|
||||
fAllValueFunc = fAllBindingsAccessor(),
|
||||
selector = fAllValueFunc ? fAllValueFunc.droppableSelector : '',
|
||||
droppable = selector ? document.querySelector(selector) : null,
|
||||
droppable = selector ? doc.querySelector(selector) : null,
|
||||
conf = {
|
||||
distance: 20,
|
||||
handle: '.dragHandle',
|
||||
@@ -352,7 +353,7 @@ ko.bindingHandlers.saveTrigger = {
|
||||
|
||||
$el.data(
|
||||
'save-trigger-type',
|
||||
$el.is('input[type=text],input[type=email],input[type=password],select,textarea') ? 'input' : 'custom'
|
||||
element.matches('input[type=text],input[type=email],input[type=password],select,textarea') ? 'input' : 'custom'
|
||||
);
|
||||
|
||||
if ('custom' === $el.data('save-trigger-type')) {
|
||||
|
||||
@@ -53,7 +53,7 @@ export class AbstractViewNext {
|
||||
closeCommand() {} // eslint-disable-line no-empty-function
|
||||
|
||||
querySelector(selectors) {
|
||||
return this.viewModelDom[0].querySelector(selectors);
|
||||
return this.viewModelDom.querySelector(selectors);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,7 +7,8 @@ import { $htmlCL, VIEW_MODELS } from 'Common/Globals';
|
||||
let currentScreen = null,
|
||||
defaultScreenName = '';
|
||||
|
||||
const SCREENS = {}, $ = jQuery,
|
||||
const SCREENS = {},
|
||||
qs = s => document.querySelector(s),
|
||||
isNonEmptyArray = values => Array.isArray(values) && values.length,
|
||||
|
||||
popupVisibilityNames = ko.observableArray([]);
|
||||
@@ -29,10 +30,8 @@ export const ViewType = {
|
||||
* @returns {void}
|
||||
*/
|
||||
export function hideLoading() {
|
||||
$('#rl-content').addClass('rl-content-show');
|
||||
$('#rl-loading')
|
||||
.hide()
|
||||
.remove();
|
||||
qs('#rl-content').classList.add('rl-content-show');
|
||||
qs('#rl-loading').remove();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -156,7 +155,7 @@ export function buildViewModel(ViewModelClass, vmScreen) {
|
||||
let vmDom = null;
|
||||
const vm = new ViewModelClass(vmScreen),
|
||||
position = ViewModelClass.__type || '',
|
||||
vmPlace = position ? $('#rl-content #rl-' + position.toLowerCase()) : null;
|
||||
vmPlace = position ? qs('#rl-content #rl-' + position.toLowerCase()) : null;
|
||||
|
||||
ViewModelClass.__builded = true;
|
||||
ViewModelClass.__vm = vm;
|
||||
@@ -166,15 +165,14 @@ export function buildViewModel(ViewModelClass, vmScreen) {
|
||||
vm.viewModelTemplateID = ViewModelClass.__templateID;
|
||||
vm.viewModelPosition = ViewModelClass.__type;
|
||||
|
||||
if (vmPlace && 1 === vmPlace.length) {
|
||||
vmDom = $('<div></div>')
|
||||
.addClass('rl-view-model')
|
||||
.addClass('RL-' + vm.viewModelTemplateID)
|
||||
.hide();
|
||||
vmDom.appendTo(vmPlace);
|
||||
if (vmPlace) {
|
||||
vmDom = jQuery('<div></div>');
|
||||
vmDom[0].classList.add('rl-view-model', 'RL-' + vm.viewModelTemplateID);
|
||||
vmDom[0].hidden = true;
|
||||
vmPlace.append(vmDom[0]);
|
||||
|
||||
vm.viewModelDom = vmDom;
|
||||
ViewModelClass.__dom = vmDom;
|
||||
vm.viewModelDom = vmDom[0];
|
||||
ViewModelClass.__dom = vmDom[0];
|
||||
|
||||
if (ViewType.Popup === position) {
|
||||
vm.cancelCommand = vm.closeCommand = createCommand(() => {
|
||||
@@ -183,11 +181,11 @@ export function buildViewModel(ViewModelClass, vmScreen) {
|
||||
|
||||
vm.modalVisibility.subscribe((value) => {
|
||||
if (value) {
|
||||
vm.viewModelDom.show();
|
||||
vm.viewModelDom.hidden = false;
|
||||
vm.storeAndSetKeyScope();
|
||||
|
||||
popupVisibilityNames.push(vm.viewModelName);
|
||||
vm.viewModelDom.css('z-index', 3000 + popupVisibilityNames().length + 10);
|
||||
vm.viewModelDom.style.zIndex = 3000 + popupVisibilityNames().length + 10;
|
||||
|
||||
vm.onShowWithDelay && setTimeout(()=>vm.onShowWithDelay, 500);
|
||||
} else {
|
||||
@@ -197,9 +195,9 @@ export function buildViewModel(ViewModelClass, vmScreen) {
|
||||
vm.restoreKeyScope();
|
||||
|
||||
popupVisibilityNames.remove(vm.viewModelName);
|
||||
vm.viewModelDom.css('z-index', 2000);
|
||||
vm.viewModelDom.style.zIndex = 2000;
|
||||
|
||||
setTimeout(() => vm.viewModelDom.hide(), 300);
|
||||
setTimeout(() => vm.viewModelDom.hidden = true, 300);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -245,7 +243,7 @@ export function showScreenPopup(ViewModelClassToShow, params = []) {
|
||||
ModalView.__vm.onShow && ModalView.__vm.onShow(...params);
|
||||
|
||||
// if (!bMobileDevice) {
|
||||
const af = ModalView.__dom[0].querySelector('[autofocus]');
|
||||
const af = ModalView.__dom.querySelector('[autofocus]');
|
||||
af && af.focus();
|
||||
}
|
||||
}
|
||||
@@ -327,7 +325,7 @@ export function screenOnRoute(screenName, subPart) {
|
||||
ViewModelClass.__dom &&
|
||||
ViewType.Popup !== ViewModelClass.__vm.viewModelPosition
|
||||
) {
|
||||
ViewModelClass.__dom.hide();
|
||||
ViewModelClass.__dom.hidden = true;
|
||||
ViewModelClass.__vm.viewModelVisibility(false);
|
||||
|
||||
ViewModelClass.__vm.onHide && ViewModelClass.__vm.onHide();
|
||||
@@ -353,13 +351,13 @@ export function screenOnRoute(screenName, subPart) {
|
||||
) {
|
||||
ViewModelClass.__vm.onBeforeShow && ViewModelClass.__vm.onBeforeShow();
|
||||
|
||||
ViewModelClass.__dom.show();
|
||||
ViewModelClass.__dom.hidden = false;
|
||||
ViewModelClass.__vm.viewModelVisibility(true);
|
||||
|
||||
ViewModelClass.__vm.onShow && ViewModelClass.__vm.onShow();
|
||||
|
||||
// if (!bMobileDevice) {
|
||||
const af = ViewModelClass.__dom[0].querySelector('[autofocus]');
|
||||
const af = ViewModelClass.__dom.querySelector('[autofocus]');
|
||||
af && af.focus();
|
||||
|
||||
ViewModelClass.__vm.onShowWithDelay && setTimeout(()=>ViewModelClass.__vm.onShowWithDelay, 200);
|
||||
|
||||
@@ -5,7 +5,8 @@ import { i18n } from 'Common/Translator';
|
||||
|
||||
import {
|
||||
pInt,
|
||||
previewMessage,
|
||||
deModule,
|
||||
encodeHtml,
|
||||
friendlySize,
|
||||
isNonEmptyArray
|
||||
} from 'Common/Utils';
|
||||
@@ -20,7 +21,7 @@ import { emailArrayFromJson, emailArrayToStringClear, emailArrayToString, replyH
|
||||
import { AttachmentModel, staticCombinedIconClass } from 'Model/Attachment';
|
||||
import { AbstractModel } from 'Knoin/AbstractModel';
|
||||
|
||||
const $ = jQuery, isArray = Array.isArray;
|
||||
const isArray = Array.isArray;
|
||||
|
||||
class MessageModel extends AbstractModel {
|
||||
constructor() {
|
||||
@@ -604,13 +605,6 @@ class MessageModel extends AbstractModel {
|
||||
return [toResult, ccResult];
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string}
|
||||
*/
|
||||
textBodyToString() {
|
||||
return this.body ? this.body.html() : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string}
|
||||
*/
|
||||
@@ -625,24 +619,30 @@ class MessageModel extends AbstractModel {
|
||||
viewPopupMessage(print = false) {
|
||||
const timeStampInUTC = this.dateTimeStampInUTC() || 0,
|
||||
ccLine = this.ccToLine(false),
|
||||
m = 0 < timeStampInUTC ? new Date(timeStampInUTC * 1000) : null;
|
||||
m = 0 < timeStampInUTC ? new Date(timeStampInUTC * 1000) : null,
|
||||
win = open(''),
|
||||
doc = win.document;
|
||||
|
||||
previewMessage(
|
||||
{
|
||||
title: this.subject(),
|
||||
subject: this.subject(),
|
||||
date: m ? m.format('LLL') : '',
|
||||
fromCreds: this.fromToLine(false),
|
||||
toLabel: i18n('MESSAGE/LABEL_TO'),
|
||||
toCreds: this.toToLine(false),
|
||||
ccClass: ccLine ? '' : 'rl-preview-hide',
|
||||
ccLabel: i18n('MESSAGE/LABEL_CC'),
|
||||
ccCreds: ccLine
|
||||
},
|
||||
this.body,
|
||||
this.isHtml(),
|
||||
print
|
||||
doc.write(
|
||||
deModule(require('Html/PreviewMessage.html'))
|
||||
.replace('{{title}}', encodeHtml(this.subject()))
|
||||
.replace('{{subject}}', encodeHtml(this.subject()))
|
||||
.replace('{{date}}', encodeHtml(m ? m.format('LLL') : ''))
|
||||
.replace('{{fromCreds}}', encodeHtml(this.fromToLine(false)))
|
||||
.replace('{{toCreds}}', encodeHtml(this.toToLine(false)))
|
||||
.replace('{{toLabel}}', encodeHtml(i18n('MESSAGE/LABEL_TO')))
|
||||
.replace('{{ccClass}}', encodeHtml(ccLine ? '' : 'rl-preview-hide'))
|
||||
.replace('{{ccCreds}}', encodeHtml(ccLine))
|
||||
.replace('{{ccLabel}}', encodeHtml(i18n('MESSAGE/LABEL_CC')))
|
||||
.replace('{{bodyClass}}', this.isHtml() ? 'html' : 'plain')
|
||||
.replace('{{html}}', this.bodyAsHTML())
|
||||
);
|
||||
|
||||
doc.close();
|
||||
|
||||
if (print) {
|
||||
setTimeout(() => win.print(), 100);
|
||||
}
|
||||
}
|
||||
|
||||
printMessage() {
|
||||
@@ -725,74 +725,60 @@ class MessageModel extends AbstractModel {
|
||||
return this;
|
||||
}
|
||||
|
||||
showExternalImages(lazy = false) {
|
||||
if (this.body && this.body.data('rl-has-images')) {
|
||||
showExternalImages() {
|
||||
if (this.body && this.body.rlHasImages) {
|
||||
this.hasImages(false);
|
||||
this.body.data('rl-has-images', false);
|
||||
this.body.rlHasImages = false;
|
||||
|
||||
let attr = this.proxy ? 'data-x-additional-src' : 'data-x-src';
|
||||
$('[' + attr + ']', this.body).each(function() {
|
||||
const $this = $(this); // eslint-disable-line no-invalid-this
|
||||
if (lazy && $this.is('img')) {
|
||||
$this.attr('loading', 'lazy');
|
||||
let body = this.body, attr = this.proxy ? 'data-x-additional-src' : 'data-x-src';
|
||||
body.querySelectorAll('[' + attr + ']').forEach(node => {
|
||||
if (node.matches('img')) {
|
||||
node.loading = 'lazy';
|
||||
}
|
||||
$this.attr('src', $this.attr(attr)).removeAttr('data-loaded');
|
||||
node.src = node.getAttribute(attr);
|
||||
node.removeAttribute('data-loaded');
|
||||
});
|
||||
|
||||
attr = this.proxy ? 'data-x-additional-style-url' : 'data-x-style-url';
|
||||
$('[' + attr + ']', this.body).each(function() {
|
||||
const $this = $(this); // eslint-disable-line no-invalid-this
|
||||
let style = $this.attr('style').trim();
|
||||
style = style ? (';' === style.substr(-1) ? style + ' ' : style + '; ') : '';
|
||||
$this.attr('style', style + $this.attr(attr));
|
||||
body.querySelectorAll('[' + attr + ']').forEach(node => {
|
||||
node.setAttribute('style', ((node.getAttribute('style')||'')
|
||||
+ ';' + node.getAttribute(attr))
|
||||
.replace(/^[;\s]+/,''));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
showInternalImages(lazy = false) {
|
||||
if (this.body && !this.body.data('rl-init-internal-images')) {
|
||||
this.body.data('rl-init-internal-images', true);
|
||||
showInternalImages() {
|
||||
if (this.body && !this.body.rlInitInternalImages) {
|
||||
this.body.rlInitInternalImages = true;
|
||||
|
||||
const self = this;
|
||||
|
||||
$('[data-x-src-cid]', this.body).each(function() {
|
||||
const $this = $(this), // eslint-disable-line no-invalid-this
|
||||
attachment = self.findAttachmentByCid($this.attr('data-x-src-cid'));
|
||||
const body = this.body;
|
||||
|
||||
body.querySelectorAll('[data-x-src-cid]').forEach(node => {
|
||||
const attachment = this.findAttachmentByCid(node.dataset.xSrcCid);
|
||||
if (attachment && attachment.download) {
|
||||
$this.attr('src', attachment.linkPreview());
|
||||
node.src = attachment.linkPreview();
|
||||
}
|
||||
});
|
||||
|
||||
$('[data-x-src-location]', this.body).each(function() {
|
||||
const $this = $(this); // eslint-disable-line no-invalid-this
|
||||
let attachment = self.findAttachmentByContentLocation($this.attr('data-x-src-location'));
|
||||
if (!attachment) {
|
||||
attachment = self.findAttachmentByCid($this.attr('data-x-src-location'));
|
||||
}
|
||||
|
||||
body.querySelectorAll('[data-x-src-location]').forEach(node => {
|
||||
const attachment = this.findAttachmentByContentLocation(node.dataset.xSrcLocation)
|
||||
|| this.findAttachmentByCid(node.dataset.xSrcLocation);
|
||||
if (attachment && attachment.download) {
|
||||
if (lazy && $this.is('img')) {
|
||||
$this.attr('loading', 'lazy');
|
||||
if (node.matches('img')) {
|
||||
node.loading = 'lazy';
|
||||
}
|
||||
$this.attr('src', attachment.linkPreview());
|
||||
node.src = attachment.linkPreview();
|
||||
}
|
||||
});
|
||||
|
||||
$('[data-x-style-cid]', this.body).each(function() {
|
||||
let style = '',
|
||||
name = '';
|
||||
|
||||
const $this = $(this), // eslint-disable-line no-invalid-this
|
||||
attachment = self.findAttachmentByCid($this.attr('data-x-style-cid'));
|
||||
|
||||
if (attachment && attachment.linkPreview) {
|
||||
name = $this.attr('data-x-style-cid-name');
|
||||
if (name) {
|
||||
style = $this.attr('style').trim();
|
||||
style = style ? (';' === style.substr(-1) ? style + ' ' : style + '; ') : '';
|
||||
$this.attr('style', style + name + ": url('" + attachment.linkPreview() + "')");
|
||||
}
|
||||
body.querySelectorAll('[style-cid]').forEach(node => {
|
||||
const name = node.dataset.xStyleCidName,
|
||||
attachment = this.findAttachmentByCid(node.dataset.xStyleCid);
|
||||
if (attachment && attachment.linkPreview && name) {
|
||||
node.setAttribute('style', ((node.getAttribute('style')||'')
|
||||
+ ';' + name + ": url('" + attachment.linkPreview() + "')")
|
||||
.replace(/^[;\s]+/,''));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -800,22 +786,37 @@ class MessageModel extends AbstractModel {
|
||||
|
||||
storeDataInDom() {
|
||||
if (this.body) {
|
||||
this.body.data('rl-is-html', !!this.isHtml());
|
||||
this.body.data('rl-has-images', !!this.hasImages());
|
||||
this.body.rlIsHtml = !!this.isHtml();
|
||||
this.body.rlHasImages = !!this.hasImages();
|
||||
}
|
||||
}
|
||||
|
||||
fetchDataFromDom() {
|
||||
if (this.body) {
|
||||
this.isHtml(!!this.body.data('rl-is-html'));
|
||||
this.hasImages(!!this.body.data('rl-has-images'));
|
||||
this.isHtml(!!this.body.rlIsHtml);
|
||||
this.hasImages(!!this.body.rlHasImages);
|
||||
}
|
||||
}
|
||||
|
||||
replacePlaneTextBody(plain) {
|
||||
/**
|
||||
* @returns {string}
|
||||
*/
|
||||
bodyAsHTML() {
|
||||
if (this.body) {
|
||||
this.body.html(plain).addClass('b-text-part plain');
|
||||
let clone = this.body.cloneNode(true),
|
||||
attr = 'data-html-editor-font-wrapper';
|
||||
clone.querySelectorAll('blockquote.rl-bq-switcher').forEach(
|
||||
node => node.classList.remove('rl-bq-switcher','hidden-bq')
|
||||
);
|
||||
clone.querySelectorAll('.rlBlockquoteSwitcher').forEach(
|
||||
node => node.remove()
|
||||
);
|
||||
clone.querySelectorAll('['+attr+']').forEach(
|
||||
node => node.removeAttribute(attr)
|
||||
);
|
||||
return clone.innerHTML;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -116,7 +116,6 @@ class AbstractAjaxRemote {
|
||||
* @param {?number=} iTimeOut = 20000
|
||||
* @param {string=} sGetAdd = ''
|
||||
* @param {Array=} aAbortActions = []
|
||||
* @returns {jQuery.jqXHR}
|
||||
*/
|
||||
ajaxRequest(fResultCallback, params, iTimeOut = 20000, sGetAdd = '', abortActions = []) {
|
||||
params = params || {};
|
||||
|
||||
@@ -34,7 +34,6 @@ class AbstractSettingsScreen extends AbstractScreen {
|
||||
onRoute(subName) {
|
||||
let settingsScreen = null,
|
||||
RoutedSettingsViewModel = null,
|
||||
viewModelPlace = null,
|
||||
viewModelDom = null;
|
||||
|
||||
RoutedSettingsViewModel = VIEW_MODELS.settings.find(
|
||||
@@ -67,20 +66,19 @@ class AbstractSettingsScreen extends AbstractScreen {
|
||||
if (RoutedSettingsViewModel.__builded && RoutedSettingsViewModel.__vm) {
|
||||
settingsScreen = RoutedSettingsViewModel.__vm;
|
||||
} else {
|
||||
viewModelPlace = this.oViewModelPlace;
|
||||
if (viewModelPlace && 1 === viewModelPlace.length) {
|
||||
if (this.oViewModelPlace) {
|
||||
settingsScreen = new RoutedSettingsViewModel();
|
||||
|
||||
viewModelDom = jQuery('<div></div>')
|
||||
.addClass('rl-settings-view-model')
|
||||
.hide();
|
||||
viewModelDom.appendTo(viewModelPlace);
|
||||
viewModelDom = jQuery('<div></div>');
|
||||
viewModelDom[0].classList.add('rl-settings-view-model');
|
||||
viewModelDom[0].hidden = true;
|
||||
this.oViewModelPlace.append(viewModelDom[0]);
|
||||
|
||||
settingsScreen.viewModelDom = viewModelDom;
|
||||
settingsScreen.viewModelDom = viewModelDom[0];
|
||||
|
||||
settingsScreen.__rlSettingsData = RoutedSettingsViewModel.__rlSettingsData;
|
||||
|
||||
RoutedSettingsViewModel.__dom = viewModelDom;
|
||||
RoutedSettingsViewModel.__dom = viewModelDom[0];
|
||||
RoutedSettingsViewModel.__builded = true;
|
||||
RoutedSettingsViewModel.__vm = settingsScreen;
|
||||
|
||||
@@ -106,7 +104,7 @@ class AbstractSettingsScreen extends AbstractScreen {
|
||||
// hide
|
||||
if (o.oCurrentSubScreen) {
|
||||
o.oCurrentSubScreen.onHide && o.oCurrentSubScreen.onHide();
|
||||
o.oCurrentSubScreen.viewModelDom.hide();
|
||||
o.oCurrentSubScreen.viewModelDom.hidden = true;
|
||||
}
|
||||
// --
|
||||
|
||||
@@ -115,7 +113,7 @@ class AbstractSettingsScreen extends AbstractScreen {
|
||||
// show
|
||||
if (o.oCurrentSubScreen) {
|
||||
o.oCurrentSubScreen.onBeforeShow && o.oCurrentSubScreen.onBeforeShow();
|
||||
o.oCurrentSubScreen.viewModelDom.show();
|
||||
o.oCurrentSubScreen.viewModelDom.hidden = false;
|
||||
o.oCurrentSubScreen.onShow && o.oCurrentSubScreen.onShow();
|
||||
o.oCurrentSubScreen.onShowWithDelay && setTimeout(() => o.oCurrentSubScreen.onShowWithDelay(), 200);
|
||||
|
||||
@@ -127,7 +125,7 @@ class AbstractSettingsScreen extends AbstractScreen {
|
||||
);
|
||||
});
|
||||
|
||||
jQuery('#rl-content .b-settings .b-content')[0].scrollTop = 0;
|
||||
document.querySelector('#rl-content .b-settings .b-content').scrollTop = 0;
|
||||
}
|
||||
// --
|
||||
}, 1);
|
||||
@@ -140,7 +138,7 @@ class AbstractSettingsScreen extends AbstractScreen {
|
||||
onHide() {
|
||||
if (this.oCurrentSubScreen && this.oCurrentSubScreen.viewModelDom) {
|
||||
this.oCurrentSubScreen.onHide && this.oCurrentSubScreen.onHide();
|
||||
this.oCurrentSubScreen.viewModelDom.hide();
|
||||
this.oCurrentSubScreen.viewModelDom.hidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,7 +162,7 @@ class AbstractSettingsScreen extends AbstractScreen {
|
||||
}
|
||||
});
|
||||
|
||||
this.oViewModelPlace = jQuery('#rl-content #rl-settings-subscreen');
|
||||
this.oViewModelPlace = document.getElementById('rl-settings-subscreen');
|
||||
}
|
||||
|
||||
routes() {
|
||||
|
||||
@@ -128,7 +128,7 @@ class MailBoxUserScreen extends AbstractScreen {
|
||||
, 1);
|
||||
}
|
||||
|
||||
$html.on('click', '#rl-right', () => {
|
||||
jQuery($html).on('click', '#rl-right', () => {
|
||||
moveAction(false);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6,7 +6,8 @@ import {
|
||||
pInt,
|
||||
pString,
|
||||
plainToHtml,
|
||||
findEmailAndLinks
|
||||
findEmailAndLinks,
|
||||
htmlToElement
|
||||
} from 'Common/Utils';
|
||||
|
||||
import {
|
||||
@@ -44,25 +45,17 @@ import { getApp } from 'Helper/Apps/User';
|
||||
import Remote from 'Remote/User/Ajax';
|
||||
|
||||
const
|
||||
$ = jQuery,
|
||||
$div = $('<div></div>'),
|
||||
$hcont = $('<div></div>'),
|
||||
getRealHeight = $el => {
|
||||
$el
|
||||
.clone()
|
||||
.show()
|
||||
.appendTo($hcont);
|
||||
const result = $hcont.height();
|
||||
$hcont.empty();
|
||||
hcont = htmlToElement('<div area="hidden" style="position:absolute;left:-5000px"></div>'),
|
||||
getRealHeight = el => {
|
||||
hcont.innerHTML = el.outerHTML;
|
||||
const result = hcont.clientHeight;
|
||||
hcont.innerHTML = '';
|
||||
return result;
|
||||
};
|
||||
|
||||
let iMessageBodyCacheCount = 0;
|
||||
|
||||
$hcont
|
||||
.attr('area', 'hidden')
|
||||
.css({ position: 'absolute', left: -5000 })
|
||||
.appendTo($('body'));
|
||||
document.body.append(hcont);
|
||||
|
||||
class MessageUserStore {
|
||||
constructor() {
|
||||
@@ -218,12 +211,6 @@ class MessageUserStore {
|
||||
|
||||
this.messageLoading.subscribe(value => this.messageLoadingThrottle(value));
|
||||
|
||||
this.messagesBodiesDom.subscribe((dom) => {
|
||||
if (dom && !(dom instanceof $)) {
|
||||
this.messagesBodiesDom($(dom));
|
||||
}
|
||||
});
|
||||
|
||||
this.messageListEndFolder.subscribe(folder => {
|
||||
const message = this.message();
|
||||
if (message && folder && folder !== message.folderFullNameRaw) {
|
||||
@@ -233,22 +220,20 @@ class MessageUserStore {
|
||||
}
|
||||
|
||||
purgeMessageBodyCache() {
|
||||
let count = 0;
|
||||
const end = iMessageBodyCacheCount - MESSAGE_BODY_CACHE_LIMIT;
|
||||
|
||||
if (0 < end) {
|
||||
let count = 0;
|
||||
const messagesDom = this.messagesBodiesDom();
|
||||
if (messagesDom) {
|
||||
messagesDom.find('.rl-cache-class').each(function() {
|
||||
const item = $(this); // eslint-disable-line no-invalid-this
|
||||
if (end > item.data('rl-cache-count')) {
|
||||
item.addClass('rl-cache-purge');
|
||||
count += 1;
|
||||
messagesDom.querySelectorAll('.rl-cache-class').forEach(node => {
|
||||
if (end > node.rlCacheCount) {
|
||||
node.classList.add('rl-cache-purge');
|
||||
++count;
|
||||
}
|
||||
});
|
||||
|
||||
if (0 < count) {
|
||||
setTimeout(() => messagesDom.find('.rl-cache-purge').remove(), 350);
|
||||
setTimeout(() => messagesDom.querySelectorAll('.rl-cache-purge').forEach(node => node.remove()), 350);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -291,9 +276,7 @@ class MessageUserStore {
|
||||
|
||||
hideMessageBodies() {
|
||||
const messagesDom = this.messagesBodiesDom();
|
||||
if (messagesDom) {
|
||||
messagesDom.find('.b-text-part').hide();
|
||||
}
|
||||
messagesDom && messagesDom.querySelectorAll('.b-text-part').forEach(el => el.hidden = true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -441,30 +424,17 @@ class MessageUserStore {
|
||||
* @param {Object} messageTextBody
|
||||
*/
|
||||
initBlockquoteSwitcher(messageTextBody) {
|
||||
if (messageTextBody) {
|
||||
const $oList = $('blockquote:not(.rl-bq-switcher)', messageTextBody).filter(function() {
|
||||
return !$(this).parent().closest('blockquote', messageTextBody).length;
|
||||
});
|
||||
|
||||
if ($oList && $oList.length) {
|
||||
$oList.each(function() {
|
||||
const $this = $(this); // eslint-disable-line no-invalid-this
|
||||
|
||||
let h = $this.height() || getRealHeight($this);
|
||||
|
||||
if ($this.text().trim() && (0 === h || 100 < h)) {
|
||||
$this.addClass('rl-bq-switcher hidden-bq');
|
||||
$('<span class="rlBlockquoteSwitcher"><i class="icon-ellipsis" /></span>')
|
||||
.insertBefore($this)
|
||||
.on('click.rlBlockquoteSwitcher', () => {
|
||||
$this.toggleClass('hidden-bq');
|
||||
})
|
||||
.after('<br />')
|
||||
.before('<br />');
|
||||
}
|
||||
});
|
||||
messageTextBody && messageTextBody.querySelectorAll('blockquote:not(.rl-bq-switcher)').forEach(node => {
|
||||
if (node.textContent.trim() && !node.parentNode.closest('blockquote')) {
|
||||
let h = node.clientHeight || getRealHeight(node);
|
||||
if (0 === h || 100 < h) {
|
||||
const el = htmlToElement('<span class="rlBlockquoteSwitcher">•••</span>');
|
||||
node.classList.add('rl-bq-switcher','hidden-bq');
|
||||
node.before(el);
|
||||
el.addEventListener('click', () => node.classList.toggle('hidden-bq'));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -472,11 +442,9 @@ class MessageUserStore {
|
||||
* @param {Object} message
|
||||
*/
|
||||
initOpenPgpControls(messageTextBody, message) {
|
||||
if (messageTextBody && messageTextBody.find) {
|
||||
messageTextBody.find('.b-plain-openpgp:not(.inited)').each(function() {
|
||||
PgpStore.initMessageBodyControls($(this), message); // eslint-disable-line no-invalid-this
|
||||
});
|
||||
}
|
||||
messageTextBody && messageTextBody.querySelectorAll('.b-plain-openpgp:not(.inited)').forEach(node =>
|
||||
PgpStore.initMessageBodyControls(node, message)
|
||||
);
|
||||
}
|
||||
|
||||
setMessage(data, cached) {
|
||||
@@ -521,19 +489,22 @@ class MessageUserStore {
|
||||
message.initFlagsByJson(data.Result);
|
||||
}
|
||||
|
||||
messagesDom = messagesDom && messagesDom[0] ? messagesDom : null;
|
||||
if (messagesDom) {
|
||||
id = 'rl-mgs-' + message.hash.replace(/[^a-zA-Z0-9]/g, '');
|
||||
|
||||
const textBody = messagesDom.find('#' + id);
|
||||
if (!textBody || !textBody[0]) {
|
||||
const textBody = document.getElementById(id);
|
||||
if (textBody) {
|
||||
iMessageBodyCacheCount += 1;
|
||||
textBody.rlCacheCount = iMessageBodyCacheCount;
|
||||
message.fetchDataFromDom();
|
||||
} else {
|
||||
let isHtml = false;
|
||||
if (data.Result.Html) {
|
||||
isHtml = true;
|
||||
resultHtml = data.Result.Html.toString();
|
||||
} else if (data.Result.Plain) {
|
||||
isHtml = false;
|
||||
resultHtml = plainToHtml(data.Result.Plain.toString(), false);
|
||||
resultHtml = plainToHtml(data.Result.Plain.toString());
|
||||
|
||||
if ((message.isPgpSigned() || message.isPgpEncrypted()) && PgpStore.capaOpenPGP()) {
|
||||
plain = pString(data.Result.Plain);
|
||||
@@ -544,16 +515,17 @@ class MessageUserStore {
|
||||
/-----BEGIN PGP SIGNED MESSAGE-----/.test(plain) && /-----BEGIN PGP SIGNATURE-----/.test(plain);
|
||||
}
|
||||
|
||||
$div.empty();
|
||||
const pre = document.createElement('pre');
|
||||
if (pgpSigned && message.isPgpSigned()) {
|
||||
resultHtml = $div.append($('<pre class="b-plain-openpgp signed"></pre>').text(plain)).html();
|
||||
pre.className = 'b-plain-openpgp signed';
|
||||
pre.textContent = plain;
|
||||
} else if (isPgpEncrypted && message.isPgpEncrypted()) {
|
||||
resultHtml = $div.append($('<pre class="b-plain-openpgp encrypted"></pre>').text(plain)).html();
|
||||
pre.className = 'b-plain-openpgp encrypted';
|
||||
pre.textContent = plain;
|
||||
} else {
|
||||
resultHtml = '<pre>' + resultHtml + '</pre>';
|
||||
pre.innerHTML = resultHtml;
|
||||
}
|
||||
|
||||
$div.empty();
|
||||
resultHtml = pre.outerHTML;
|
||||
|
||||
message.isPgpSigned(pgpSigned);
|
||||
message.isPgpEncrypted(isPgpEncrypted);
|
||||
@@ -567,41 +539,31 @@ class MessageUserStore {
|
||||
|
||||
iMessageBodyCacheCount += 1;
|
||||
|
||||
body = $('<div id="' + id + '" ></div>')
|
||||
.hide()
|
||||
.addClass('rl-cache-class');
|
||||
body.data('rl-cache-count', iMessageBodyCacheCount);
|
||||
|
||||
body.html(findEmailAndLinks(resultHtml)).addClass('b-text-part ' + (isHtml ? 'html' : 'plain'));
|
||||
body = htmlToElement('<div id="' + id + '" hidden="" class="rl-cache-class b-text-part '
|
||||
+ (isHtml ? 'html' : 'plain') + '">'
|
||||
+ findEmailAndLinks(resultHtml)
|
||||
+ '</div>');
|
||||
body.rlCacheCount = iMessageBodyCacheCount;
|
||||
|
||||
message.isHtml(!!isHtml);
|
||||
message.hasImages(!!data.Result.HasExternals);
|
||||
|
||||
message.body = body;
|
||||
if (message.body) {
|
||||
messagesDom.append(message.body);
|
||||
}
|
||||
messagesDom.append(body);
|
||||
|
||||
message.storeDataInDom();
|
||||
|
||||
if (data.Result.HasInternals) {
|
||||
message.showInternalImages(true);
|
||||
message.showInternalImages();
|
||||
}
|
||||
|
||||
if (message.hasImages() && SettingsStore.showImages()) {
|
||||
message.showExternalImages(true);
|
||||
message.showExternalImages();
|
||||
}
|
||||
|
||||
this.purgeMessageBodyCacheThrottle();
|
||||
} else {
|
||||
message.body = textBody;
|
||||
if (message.body) {
|
||||
iMessageBodyCacheCount += 1;
|
||||
message.body.data('rl-cache-count', iMessageBodyCacheCount);
|
||||
message.fetchDataFromDom();
|
||||
}
|
||||
}
|
||||
|
||||
message.body = body || textBody;
|
||||
this.messageActiveDom(message.body);
|
||||
|
||||
this.hideMessageBodies();
|
||||
@@ -612,7 +574,7 @@ class MessageUserStore {
|
||||
this.initBlockquoteSwitcher(body);
|
||||
}
|
||||
|
||||
message.body.show();
|
||||
message.body.hidden = false;
|
||||
}
|
||||
|
||||
initMessageFlagsFromCache(message);
|
||||
|
||||
@@ -1,12 +1,134 @@
|
||||
import ko from 'ko';
|
||||
|
||||
import { i18n } from 'Common/Translator';
|
||||
import { isNonEmptyArray, pString } from 'Common/Utils';
|
||||
import { isNonEmptyArray, pString, htmlToElement } from 'Common/Utils';
|
||||
|
||||
import AccountStore from 'Stores/User/Account';
|
||||
|
||||
import { showScreenPopup } from 'Knoin/Knoin';
|
||||
|
||||
function controlsHelper(dom, verControl, success, title, text)
|
||||
{
|
||||
dom.classList.toggle('error', !success);
|
||||
dom.classList.toggle('success', success);
|
||||
verControl.classList.toggle('error', !success);
|
||||
verControl.classList.toggle('success', success);
|
||||
dom.title = verControl.title = title;
|
||||
|
||||
if (undefined !== text) {
|
||||
dom.textContent = text.trim();
|
||||
}
|
||||
}
|
||||
|
||||
function domControlEncryptedClickHelper(store, dom, armoredMessage, recipients) {
|
||||
return function() {
|
||||
let message = null;
|
||||
|
||||
if (this.classList.contains('success')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
message = store.openpgp.message.readArmored(armoredMessage);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
if (message && message.getText && message.verify && message.decrypt) {
|
||||
store.decryptMessage(
|
||||
message,
|
||||
recipients,
|
||||
(validPrivateKey, decryptedMessage, validPublicKey, signingKeyIds) => {
|
||||
if (decryptedMessage) {
|
||||
if (validPublicKey) {
|
||||
controlsHelper(
|
||||
dom,
|
||||
this,
|
||||
true,
|
||||
i18n('PGP_NOTIFICATIONS/GOOD_SIGNATURE', {
|
||||
'USER': validPublicKey.user + ' (' + validPublicKey.id + ')'
|
||||
}),
|
||||
decryptedMessage.getText()
|
||||
);
|
||||
} else if (validPrivateKey) {
|
||||
const keyIds = isNonEmptyArray(signingKeyIds) ? signingKeyIds : null,
|
||||
additional = keyIds
|
||||
? keyIds.map(item => (item && item.toHex ? item.toHex() : null)).filter(value => !!value).join(', ')
|
||||
: '';
|
||||
|
||||
controlsHelper(
|
||||
dom,
|
||||
this,
|
||||
false,
|
||||
i18n('PGP_NOTIFICATIONS/UNVERIFIRED_SIGNATURE') + (additional ? ' (' + additional + ')' : ''),
|
||||
decryptedMessage.getText()
|
||||
);
|
||||
} else {
|
||||
controlsHelper(dom, this, false, i18n('PGP_NOTIFICATIONS/DECRYPTION_ERROR'));
|
||||
}
|
||||
} else {
|
||||
controlsHelper(dom, this, false, i18n('PGP_NOTIFICATIONS/DECRYPTION_ERROR'));
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
controlsHelper(dom, this, false, i18n('PGP_NOTIFICATIONS/DECRYPTION_ERROR'));
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
function domControlSignedClickHelper(store, dom, armoredMessage) {
|
||||
return function() {
|
||||
let message = null;
|
||||
|
||||
if (this.classList.contains('success') || this.classList.contains('error')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
message = store.openpgp.cleartext.readArmored(armoredMessage);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
if (message && message.getText && message.verify) {
|
||||
store.verifyMessage(message, (validKey, signingKeyIds) => {
|
||||
if (validKey) {
|
||||
controlsHelper(
|
||||
dom,
|
||||
this,
|
||||
true,
|
||||
i18n('PGP_NOTIFICATIONS/GOOD_SIGNATURE', {
|
||||
'USER': validKey.user + ' (' + validKey.id + ')'
|
||||
}),
|
||||
message.getText()
|
||||
);
|
||||
} else {
|
||||
const keyIds = isNonEmptyArray(signingKeyIds) ? signingKeyIds : null,
|
||||
additional = keyIds
|
||||
? keyIds.map(item => (item && item.toHex ? item.toHex() : null)).filter(value => !!value).join(', ')
|
||||
: '';
|
||||
|
||||
controlsHelper(
|
||||
dom,
|
||||
this,
|
||||
false,
|
||||
i18n('PGP_NOTIFICATIONS/UNVERIFIRED_SIGNATURE') + (additional ? ' (' + additional + ')' : '')
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
controlsHelper(dom, this, false, i18n('PGP_NOTIFICATIONS/DECRYPTION_ERROR'));
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
class PgpUserStore {
|
||||
constructor() {
|
||||
this.capaOpenPGP = ko.observable(false);
|
||||
@@ -209,174 +331,34 @@ class PgpUserStore {
|
||||
return false;
|
||||
}
|
||||
|
||||
controlsHelper(dom, verControl, success, title, text) {
|
||||
if (success) {
|
||||
dom
|
||||
.removeClass('error')
|
||||
.addClass('success')
|
||||
.attr('title', title);
|
||||
verControl
|
||||
.removeClass('error')
|
||||
.addClass('success')
|
||||
.attr('title', title);
|
||||
} else {
|
||||
dom
|
||||
.removeClass('success')
|
||||
.addClass('error')
|
||||
.attr('title', title);
|
||||
verControl
|
||||
.removeClass('success')
|
||||
.addClass('error')
|
||||
.attr('title', title);
|
||||
}
|
||||
|
||||
if (undefined !== text) {
|
||||
dom.text(text.trim());
|
||||
}
|
||||
}
|
||||
|
||||
static domControlEncryptedClickHelper(store, dom, armoredMessage, recipients) {
|
||||
return function() {
|
||||
let message = null;
|
||||
const $this = jQuery(this); // eslint-disable-line no-invalid-this
|
||||
|
||||
if ($this.hasClass('success')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
message = store.openpgp.message.readArmored(armoredMessage);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
if (message && message.getText && message.verify && message.decrypt) {
|
||||
store.decryptMessage(
|
||||
message,
|
||||
recipients,
|
||||
(validPrivateKey, decryptedMessage, validPublicKey, signingKeyIds) => {
|
||||
if (decryptedMessage) {
|
||||
if (validPublicKey) {
|
||||
store.controlsHelper(
|
||||
dom,
|
||||
$this,
|
||||
true,
|
||||
i18n('PGP_NOTIFICATIONS/GOOD_SIGNATURE', {
|
||||
'USER': validPublicKey.user + ' (' + validPublicKey.id + ')'
|
||||
}),
|
||||
decryptedMessage.getText()
|
||||
);
|
||||
} else if (validPrivateKey) {
|
||||
const keyIds = isNonEmptyArray(signingKeyIds) ? signingKeyIds : null,
|
||||
additional = keyIds
|
||||
? keyIds.map(item => (item && item.toHex ? item.toHex() : null)).filter(value => !!value).join(', ')
|
||||
: '';
|
||||
|
||||
store.controlsHelper(
|
||||
dom,
|
||||
$this,
|
||||
false,
|
||||
i18n('PGP_NOTIFICATIONS/UNVERIFIRED_SIGNATURE') + (additional ? ' (' + additional + ')' : ''),
|
||||
decryptedMessage.getText()
|
||||
);
|
||||
} else {
|
||||
store.controlsHelper(dom, $this, false, i18n('PGP_NOTIFICATIONS/DECRYPTION_ERROR'));
|
||||
}
|
||||
} else {
|
||||
store.controlsHelper(dom, $this, false, i18n('PGP_NOTIFICATIONS/DECRYPTION_ERROR'));
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
store.controlsHelper(dom, $this, false, i18n('PGP_NOTIFICATIONS/DECRYPTION_ERROR'));
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
static domControlSignedClickHelper(store, dom, armoredMessage) {
|
||||
return function() {
|
||||
let message = null;
|
||||
const $this = jQuery(this); // eslint-disable-line no-invalid-this
|
||||
|
||||
if ($this.hasClass('success') || $this.hasClass('error')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
message = store.openpgp.cleartext.readArmored(armoredMessage);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
if (message && message.getText && message.verify) {
|
||||
store.verifyMessage(message, (validKey, signingKeyIds) => {
|
||||
if (validKey) {
|
||||
store.controlsHelper(
|
||||
dom,
|
||||
$this,
|
||||
true,
|
||||
i18n('PGP_NOTIFICATIONS/GOOD_SIGNATURE', {
|
||||
'USER': validKey.user + ' (' + validKey.id + ')'
|
||||
}),
|
||||
message.getText()
|
||||
);
|
||||
} else {
|
||||
const keyIds = isNonEmptyArray(signingKeyIds) ? signingKeyIds : null,
|
||||
additional = keyIds
|
||||
? keyIds.map(item => (item && item.toHex ? item.toHex() : null)).filter(value => !!value).join(', ')
|
||||
: '';
|
||||
|
||||
store.controlsHelper(
|
||||
dom,
|
||||
$this,
|
||||
false,
|
||||
i18n('PGP_NOTIFICATIONS/UNVERIFIRED_SIGNATURE') + (additional ? ' (' + additional + ')' : '')
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
store.controlsHelper(dom, $this, false, i18n('PGP_NOTIFICATIONS/DECRYPTION_ERROR'));
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {*} dom
|
||||
* @param {MessageModel} rainLoopMessage
|
||||
*/
|
||||
initMessageBodyControls(dom, rainLoopMessage) {
|
||||
if (dom && !dom.hasClass('inited')) {
|
||||
dom.addClass('inited');
|
||||
const cl = dom && dom.classList;
|
||||
if (!cl.has('inited')) {
|
||||
cl.add('inited');
|
||||
|
||||
const encrypted = dom.hasClass('encrypted'),
|
||||
signed = dom.hasClass('signed'),
|
||||
const encrypted = cl.has('encrypted'),
|
||||
signed = cl.has('signed'),
|
||||
recipients = rainLoopMessage ? rainLoopMessage.getEmails(['from', 'to', 'cc']) : [];
|
||||
|
||||
let verControl = null;
|
||||
|
||||
if (encrypted || signed) {
|
||||
const domText = dom.text();
|
||||
dom.data('openpgp-original', domText);
|
||||
const domText = dom.textContent;
|
||||
|
||||
verControl = htmlToElement('<div class="b-openpgp-control"><i class="icon-lock"></i></div>'); // 🔒
|
||||
if (encrypted) {
|
||||
verControl = jQuery('<div class="b-openpgp-control"><i class="icon-lock"></i></div>')
|
||||
.attr('title', i18n('MESSAGE/PGP_ENCRYPTED_MESSAGE_DESC'))
|
||||
.on('click', PgpUserStore.domControlEncryptedClickHelper(this, dom, domText, recipients));
|
||||
} else if (signed) {
|
||||
verControl = jQuery('<div class="b-openpgp-control"><i class="icon-lock"></i></div>')
|
||||
.attr('title', i18n('MESSAGE/PGP_SIGNED_MESSAGE_DESC'))
|
||||
.on('click', PgpUserStore.domControlSignedClickHelper(this, dom, domText));
|
||||
verControl.title = i18n('MESSAGE/PGP_ENCRYPTED_MESSAGE_DESC');
|
||||
verControl.addEventHandler('click', domControlEncryptedClickHelper(this, dom, domText, recipients));
|
||||
} else {
|
||||
verControl.title = i18n('MESSAGE/PGP_SIGNED_MESSAGE_DESC');
|
||||
verControl.addEventHandler('click', domControlSignedClickHelper(this, dom, domText));
|
||||
}
|
||||
|
||||
if (verControl) {
|
||||
dom.before(verControl).before('<div></div>');
|
||||
}
|
||||
dom.before(verControl, document.createElement('div'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import ko from 'ko';
|
||||
|
||||
import { MESSAGES_PER_PAGE, MESSAGES_PER_PAGE_VALUES } from 'Common/Consts';
|
||||
import { Layout, EditorDefaultType } from 'Common/Enums';
|
||||
import { $htmlCL } from 'Common/Globals';
|
||||
import { pInt } from 'Common/Utils';
|
||||
|
||||
import * as Settings from 'Storage/Settings';
|
||||
@@ -43,10 +42,11 @@ class SettingsUserStore {
|
||||
}
|
||||
|
||||
subscribers() {
|
||||
this.layout.subscribe((value) => {
|
||||
$htmlCL.toggle('rl-no-preview-pane', Layout.NoPreview === value);
|
||||
$htmlCL.toggle('rl-side-preview-pane', Layout.SidePreview === value);
|
||||
$htmlCL.toggle('rl-bottom-preview-pane', Layout.BottomPreview === value);
|
||||
const htmlCL = document.documentElement.classList;
|
||||
this.layout.subscribe(value => {
|
||||
htmlCL.toggle('rl-no-preview-pane', Layout.NoPreview === value);
|
||||
htmlCL.toggle('rl-side-preview-pane', Layout.SidePreview === value);
|
||||
htmlCL.toggle('rl-bottom-preview-pane', Layout.BottomPreview === value);
|
||||
dispatchEvent(new CustomEvent('rl-layout', {detail:value}));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -376,13 +376,13 @@ html.rl-no-preview-pane {
|
||||
|
||||
background-color: #eee;
|
||||
border: 1px solid #999;
|
||||
display: inline-block;
|
||||
display: block;
|
||||
width: 30px;
|
||||
height: 14px;
|
||||
line-height: 14px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
margin: 10px 0px;
|
||||
margin: 2em 0 10px;
|
||||
|
||||
opacity: 0.5;
|
||||
&:hover {
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import ko from 'ko';
|
||||
|
||||
import { triggerAutocompleteInputChange } from 'Common/Utils';
|
||||
|
||||
import { StorageResultType, Notification } from 'Common/Enums';
|
||||
import { getNotification } from 'Common/Translator';
|
||||
|
||||
@@ -37,8 +35,6 @@ class LoginAdminView extends AbstractViewNext {
|
||||
this.loginErrorAnimation = ko.observable(false).extend({ 'falseTimeout': 500 });
|
||||
this.passwordErrorAnimation = ko.observable(false).extend({ 'falseTimeout': 500 });
|
||||
|
||||
// this.loginFocus = ko.observable(false);
|
||||
|
||||
this.formHidden = ko.observable(false);
|
||||
|
||||
this.formError = ko.computed(() => this.loginErrorAnimation() || this.passwordErrorAnimation());
|
||||
@@ -59,8 +55,6 @@ class LoginAdminView extends AbstractViewNext {
|
||||
|
||||
@command((self) => !self.submitRequest())
|
||||
submitCommand() {
|
||||
triggerAutocompleteInputChange();
|
||||
|
||||
this.loginError(false);
|
||||
this.passwordError(false);
|
||||
|
||||
@@ -98,13 +92,6 @@ class LoginAdminView extends AbstractViewNext {
|
||||
routeOff();
|
||||
}
|
||||
|
||||
onHide() {
|
||||
}
|
||||
|
||||
onBuild() {
|
||||
triggerAutocompleteInputChange(true);
|
||||
}
|
||||
|
||||
submitForm() {
|
||||
this.submitCommand();
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
|
||||
import {
|
||||
isNonEmptyArray,
|
||||
clearBqSwitcher,
|
||||
replySubjectAdd,
|
||||
encodeHtml,
|
||||
inFocus,
|
||||
@@ -855,13 +854,7 @@ class ComposePopupView extends AbstractViewNext {
|
||||
sDate = momentorFormat(message.dateTimeStampInUTC(), 'FULL');
|
||||
sSubject = message.subject();
|
||||
aDraftInfo = message.aDraftInfo;
|
||||
|
||||
const clonedText = jQuery(message.body).clone();
|
||||
if (clonedText) {
|
||||
clearBqSwitcher(clonedText);
|
||||
|
||||
sText = clonedText.html();
|
||||
}
|
||||
sText = message.bodyAsHTML();
|
||||
|
||||
let resplyAllParts = null;
|
||||
switch (lineComposeType) {
|
||||
|
||||
@@ -610,9 +610,7 @@ class ContactsPopupView extends AbstractViewNext {
|
||||
}
|
||||
|
||||
onBuild(dom) {
|
||||
this.oContentVisible = jQuery('.b-list-content', dom);
|
||||
|
||||
this.selector.init(this.oContentVisible, this.oContentVisible, KeyState.ContactList);
|
||||
this.selector.init(dom[0].querySelector('.b-list-content'), KeyState.ContactList);
|
||||
|
||||
key('delete', KeyState.ContactList, () => {
|
||||
this.deleteCommand();
|
||||
|
||||
@@ -120,10 +120,8 @@ class MessageOpenPgpPopupView extends AbstractViewNext {
|
||||
this.privateKeys(privateKeys);
|
||||
|
||||
if (this.viewModelDom) {
|
||||
this.viewModelDom
|
||||
.find('.key-list__item')
|
||||
.first()
|
||||
.click();
|
||||
const el = this.viewModelDom.querySelector('.key-list__item');
|
||||
el && el.click();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
Notification
|
||||
} from 'Common/Enums';
|
||||
|
||||
import { convertLangName, triggerAutocompleteInputChange } from 'Common/Utils';
|
||||
import { convertLangName } from 'Common/Utils';
|
||||
|
||||
import { getNotification, getNotificationFromResponse, reload as translatorReload } from 'Common/Translator';
|
||||
|
||||
@@ -72,9 +72,6 @@ class LoginUserView extends AbstractViewNext {
|
||||
(this.additionalCode.visibility() && this.additionalCode.errorAnimation())
|
||||
);
|
||||
|
||||
// this.emailFocus = ko.observable(false);
|
||||
// this.passwordFocus = ko.observable(false);
|
||||
|
||||
this.email.subscribe(() => {
|
||||
this.emailError(false);
|
||||
this.additionalCode('');
|
||||
@@ -144,8 +141,6 @@ class LoginUserView extends AbstractViewNext {
|
||||
|
||||
@command((self) => !self.submitRequest())
|
||||
submitCommand() {
|
||||
triggerAutocompleteInputChange();
|
||||
|
||||
this.emailError(false);
|
||||
this.passwordError(false);
|
||||
|
||||
@@ -280,8 +275,6 @@ class LoginUserView extends AbstractViewNext {
|
||||
);
|
||||
});
|
||||
}, 50);
|
||||
|
||||
triggerAutocompleteInputChange(true);
|
||||
}
|
||||
|
||||
submitForm() {
|
||||
@@ -291,16 +284,6 @@ class LoginUserView extends AbstractViewNext {
|
||||
selectLanguage() {
|
||||
showScreenPopup(require('View/Popup/Languages'), [this.language, this.languages(), LanguageStore.userLanguage()]);
|
||||
}
|
||||
|
||||
selectLanguageOnTab(bShift) {
|
||||
if (!bShift) {
|
||||
// setTimeout(() => this.emailFocus(true), 50);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export { LoginUserView, LoginUserView as default };
|
||||
|
||||
@@ -17,8 +17,6 @@ import { getApp } from 'Helper/Apps/User';
|
||||
import { view, ViewType, showScreenPopup, setHash } from 'Knoin/Knoin';
|
||||
import { AbstractViewNext } from 'Knoin/AbstractViewNext';
|
||||
|
||||
const $ = jQuery;
|
||||
|
||||
@view({
|
||||
name: 'View/User/MailBox/FolderList',
|
||||
type: ViewType.Left,
|
||||
@@ -28,7 +26,6 @@ class FolderListMailBoxUserView extends AbstractViewNext {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.oContentVisible = null;
|
||||
this.oContentScrollable = null;
|
||||
|
||||
this.composeInEdit = AppStore.composeInEdit;
|
||||
@@ -61,10 +58,8 @@ class FolderListMailBoxUserView extends AbstractViewNext {
|
||||
}
|
||||
|
||||
onBuild(dom) {
|
||||
this.oContentVisible = $('.b-content', dom);
|
||||
this.oContentScrollable = this.oContentVisible ? this.oContentVisible[0] : null;
|
||||
|
||||
const self = this,
|
||||
qs = s => dom[0].querySelector(s),
|
||||
isMobile = Settings.appSettingsGet('mobile'),
|
||||
fSelectFolder = (el, event, starred) => {
|
||||
const isMove = moveAction();
|
||||
@@ -108,6 +103,8 @@ class FolderListMailBoxUserView extends AbstractViewNext {
|
||||
}
|
||||
};
|
||||
|
||||
this.oContentScrollable = qs('.b-content');
|
||||
|
||||
dom
|
||||
.on('click', '.b-folders .e-item .e-link .e-collapsed-sign', function(event) {
|
||||
// eslint-disable-line prefer-arrow-callback
|
||||
@@ -132,21 +129,22 @@ class FolderListMailBoxUserView extends AbstractViewNext {
|
||||
|
||||
key('up, down', KeyState.FolderList, (event, handler) => {
|
||||
const keyCode = handler && 'up' === handler.shortcut ? EventKeyCode.Up : EventKeyCode.Down,
|
||||
$items = $('.b-folders .e-item .e-link:not(.hidden):visible', dom);
|
||||
|
||||
if (event && $items.length) {
|
||||
let index = $items.index($items.filter('.focused'));
|
||||
if (-1 < index) {
|
||||
$items.eq(index).removeClass('focused');
|
||||
$items = jQuery('.b-folders .e-item .e-link:not(.hidden):visible', dom);
|
||||
let index = $items.length;
|
||||
if (event && index) {
|
||||
while (index--) {
|
||||
if ($items[index].matches('.focused')) {
|
||||
$items[index].classList.remove('focused');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (EventKeyCode.Up === keyCode && 0 < index) {
|
||||
index -= 1;
|
||||
--index;
|
||||
} else if (EventKeyCode.Down === keyCode && index < $items.length - 1) {
|
||||
index += 1;
|
||||
++index;
|
||||
}
|
||||
|
||||
$items.eq(index).addClass('focused');
|
||||
$items[index].classList.add('focused');
|
||||
self.scrollToFocused();
|
||||
}
|
||||
|
||||
@@ -154,24 +152,22 @@ class FolderListMailBoxUserView extends AbstractViewNext {
|
||||
});
|
||||
|
||||
key('enter', KeyState.FolderList, () => {
|
||||
const $items = $('.b-folders .e-item .e-link:not(.hidden).focused', dom);
|
||||
if ($items.length && $items[0]) {
|
||||
const item = qs('.b-folders .e-item .e-link:not(.hidden).focused');
|
||||
if (item) {
|
||||
AppStore.focusedState(Focused.MessageList);
|
||||
$items.click();
|
||||
item.click();
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
key('space', KeyState.FolderList, () => {
|
||||
const $items = $('.b-folders .e-item .e-link:not(.hidden).focused', dom);
|
||||
if ($items.length && $items[0]) {
|
||||
const folder = ko.dataFor($items[0]);
|
||||
if (folder) {
|
||||
const collapsed = folder.collapsed();
|
||||
getApp().setExpandedFolder(folder.fullNameHash, collapsed);
|
||||
folder.collapsed(!collapsed);
|
||||
}
|
||||
const item = qs('.b-folders .e-item .e-link:not(.hidden).focused'),
|
||||
folder = item && ko.dataFor(item);
|
||||
if (folder) {
|
||||
const collapsed = folder.collapsed();
|
||||
getApp().setExpandedFolder(folder.fullNameHash, collapsed);
|
||||
folder.collapsed(!collapsed);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -183,10 +179,12 @@ class FolderListMailBoxUserView extends AbstractViewNext {
|
||||
return false;
|
||||
});
|
||||
|
||||
AppStore.focusedState.subscribe((value) => {
|
||||
$('.b-folders .e-item .e-link.focused', dom).removeClass('focused');
|
||||
AppStore.focusedState.subscribe(value => {
|
||||
let el = qs('.b-folders .e-item .e-link.focused');
|
||||
el && qs('.b-folders .e-item .e-link.focused').classList.remove('focused');
|
||||
if (Focused.FolderList === value) {
|
||||
$('.b-folders .e-item .e-link.selected', dom).addClass('focused');
|
||||
el = qs('.b-folders .e-item .e-link.selected');
|
||||
el && qs('.b-folders .e-item .e-link.selected').classList.add('focused');
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -206,28 +204,20 @@ class FolderListMailBoxUserView extends AbstractViewNext {
|
||||
}
|
||||
|
||||
scrollToFocused() {
|
||||
if (!this.oContentVisible || !this.oContentScrollable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const offset = 20,
|
||||
focused = $('.e-item .e-link.focused', this.oContentScrollable),
|
||||
pos = focused.position(),
|
||||
visibleHeight = this.oContentVisible.height(),
|
||||
focusedHeight = focused.outerHeight();
|
||||
|
||||
if (pos && (0 > pos.top || pos.top + focusedHeight > visibleHeight)) {
|
||||
let top = this.oContentScrollable.scrollTop + pos.top;
|
||||
if (0 > pos.top) {
|
||||
this.oContentScrollable.scrollTop = top - offset;
|
||||
} else {
|
||||
this.oContentScrollable.scrollTop = top - visibleHeight + focusedHeight + offset;
|
||||
const scrollable = this.oContentScrollable;
|
||||
if (scrollable) {
|
||||
let block, focused = scrollable.querySelector('.e-item .e-link.focused');
|
||||
if (focused) {
|
||||
const fRect = focused.getBoundingClientRect(),
|
||||
sRect = scrollable.getBoundingClientRect();
|
||||
if (fRect.top < sRect.top) {
|
||||
block = 'start';
|
||||
} else if (fRect.bottom > sRect.bottom) {
|
||||
block = 'end';
|
||||
}
|
||||
block && focused.scrollIntoView(block === 'start');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -237,9 +227,9 @@ class FolderListMailBoxUserView extends AbstractViewNext {
|
||||
*/
|
||||
messagesDrop(toFolder, ui) {
|
||||
if (toFolder && ui && ui.helper) {
|
||||
const fromFolderFullNameRaw = ui.helper.data('rl-folder'),
|
||||
const fromFolderFullNameRaw = ui.helper.rlFolder,
|
||||
copy = $htmlCL.contains('rl-ctrl-key-pressed'),
|
||||
uids = ui.helper.data('rl-uids');
|
||||
uids = ui.helper.rlUids;
|
||||
|
||||
if (fromFolderFullNameRaw && Array.isArray(uids)) {
|
||||
getApp().moveMessagesToFolder(fromFolderFullNameRaw, uids, toFolder.fullNameRaw, copy);
|
||||
|
||||
@@ -15,7 +15,7 @@ import { UNUSED_OPTION_VALUE } from 'Common/Consts';
|
||||
|
||||
import { bMobileDevice, leftPanelDisabled, moveAction } from 'Common/Globals';
|
||||
|
||||
import { computedPagenatorHelper, draggablePlace, friendlySize } from 'Common/Utils';
|
||||
import { computedPagenatorHelper, friendlySize, htmlToElement } from 'Common/Utils';
|
||||
|
||||
import { mailBox, append } from 'Common/Links';
|
||||
import { Selector } from 'Common/Selector';
|
||||
@@ -267,9 +267,9 @@ class MessageListMailBoxUserView extends AbstractViewNext {
|
||||
}
|
||||
});
|
||||
|
||||
MessageStore.messageListEndHash.subscribe(() => {
|
||||
this.selector.scrollToTop();
|
||||
});
|
||||
MessageStore.messageListEndHash.subscribe((() =>
|
||||
this.selector.scrollToFocused()
|
||||
).throttle(50));
|
||||
}
|
||||
|
||||
@command()
|
||||
@@ -502,14 +502,20 @@ class MessageListMailBoxUserView extends AbstractViewNext {
|
||||
oMessageListItem.checked(true);
|
||||
}
|
||||
|
||||
const el = draggablePlace(),
|
||||
const el = htmlToElement('<div class="draggablePlace">' +
|
||||
'<span class="text"></span> ' +
|
||||
'<i class="icon-copy icon-white visible-on-ctrl"></i>' +
|
||||
'<i class="icon-mail icon-white hidden-on-ctrl"></i>' +
|
||||
'</div>'),
|
||||
updateUidsInfo = () => {
|
||||
const uids = MessageStore.messageListCheckedOrSelectedUidsWithSubMails();
|
||||
el.data('rl-uids', uids);
|
||||
el.find('.text').text('' + uids.length);
|
||||
el.rlUids = uids;
|
||||
el.querySelector('.text').textContent = uids.length;
|
||||
};
|
||||
|
||||
el.data('rl-folder', FolderStore.currentFolderFullNameRaw());
|
||||
document.getElementById('rl-hidden').append(el);
|
||||
|
||||
el.rlFolder = FolderStore.currentFolderFullNameRaw();
|
||||
|
||||
updateUidsInfo();
|
||||
setTimeout(updateUidsInfo,1);
|
||||
@@ -735,9 +741,7 @@ class MessageListMailBoxUserView extends AbstractViewNext {
|
||||
onBuild(dom) {
|
||||
const self = this;
|
||||
|
||||
this.oContentVisible = jQuery('.b-content', dom);
|
||||
|
||||
this.selector.init(this.oContentVisible, this.oContentVisible, KeyState.MessageList);
|
||||
this.selector.init(dom[0].querySelector('.b-content'), KeyState.MessageList);
|
||||
|
||||
if (this.mobile) {
|
||||
dom.on('click', () => {
|
||||
|
||||
@@ -243,8 +243,8 @@ class MessageViewMailBoxUserView extends AbstractViewNext {
|
||||
return '';
|
||||
});
|
||||
|
||||
this.messageActiveDom.subscribe((dom) => {
|
||||
this.bodyBackgroundColor(dom ? this.detectDomBackgroundColor(dom) : '');
|
||||
this.messageActiveDom.subscribe(dom => {
|
||||
this.bodyBackgroundColor(this.detectDomBackgroundColor(dom));
|
||||
}, this);
|
||||
|
||||
this.message.subscribe((message) => {
|
||||
@@ -357,45 +357,26 @@ class MessageViewMailBoxUserView extends AbstractViewNext {
|
||||
}
|
||||
|
||||
detectDomBackgroundColor(dom) {
|
||||
let limit = 5,
|
||||
result = '';
|
||||
let color = '';
|
||||
|
||||
const fFindDom = function(inputDom) {
|
||||
const children = inputDom ? inputDom.children() : null;
|
||||
return children && 1 === children.length && children.is('table,div,center') ? children : null;
|
||||
},
|
||||
fFindColor = function(inputDom) {
|
||||
let color = '';
|
||||
if (inputDom) {
|
||||
color = inputDom.css('background-color') || '';
|
||||
if (!inputDom.is('table')) {
|
||||
color = isTransparent(color) ? '' : color;
|
||||
}
|
||||
}
|
||||
if (dom) {
|
||||
let limit = 5,
|
||||
aC = dom;
|
||||
while (!color && aC && limit--) {
|
||||
let children = aC.children;
|
||||
if (!children || 1 !== children.length || !children[0].matches('table,div,center')) break;
|
||||
|
||||
return color;
|
||||
};
|
||||
|
||||
if (dom && 1 === dom.length) {
|
||||
let aC = dom;
|
||||
while (!result) {
|
||||
limit -= 1;
|
||||
if (0 >= limit) {
|
||||
break;
|
||||
}
|
||||
|
||||
aC = fFindDom(aC);
|
||||
if (aC) {
|
||||
result = fFindColor(aC);
|
||||
} else {
|
||||
break;
|
||||
aC = children[0];
|
||||
color = aC.style.backgroundColor || '';
|
||||
if (!aC.matches('table')) {
|
||||
color = isTransparent(color) ? '' : color;
|
||||
}
|
||||
}
|
||||
|
||||
result = isTransparent(result) ? '' : result;
|
||||
color = isTransparent(color) ? '' : color;
|
||||
}
|
||||
|
||||
return result;
|
||||
return color;
|
||||
}
|
||||
|
||||
fullScreen() {
|
||||
@@ -545,7 +526,7 @@ class MessageViewMailBoxUserView extends AbstractViewNext {
|
||||
!!event &&
|
||||
3 !== event.which &&
|
||||
mailToHelper(
|
||||
jQuery(this).attr('href'),
|
||||
this.href,
|
||||
Settings.capa(Capa.Composer) ? require('View/Popup/Compose') : null // eslint-disable-line no-invalid-this
|
||||
)
|
||||
);
|
||||
@@ -701,13 +682,11 @@ class MessageViewMailBoxUserView extends AbstractViewNext {
|
||||
|
||||
// toggle message blockquotes
|
||||
key('b', [KeyState.MessageList, KeyState.MessageView], () => {
|
||||
if (MessageStore.message() && MessageStore.message().body) {
|
||||
MessageStore.message()
|
||||
.body.find('.rlBlockquoteSwitcher')
|
||||
.click();
|
||||
const message = MessageStore.message();
|
||||
if (message && message.body) {
|
||||
message.body.querySelectorAll('.rlBlockquoteSwitcher').forEach(node => node.click());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -878,7 +857,7 @@ class MessageViewMailBoxUserView extends AbstractViewNext {
|
||||
*/
|
||||
showImages(message) {
|
||||
if (message && message.showExternalImages) {
|
||||
message.showExternalImages(true);
|
||||
message.showExternalImages();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ progress.className = "progressjs-inner";
|
||||
progress.appendChild(doc.createElement('div')).className = "progressjs-percent";
|
||||
|
||||
setPercentWidth(1);
|
||||
doc.body.appendChild(container);
|
||||
doc.body.append(container);
|
||||
|
||||
win.progressJs = new class {
|
||||
set(percent) {
|
||||
@@ -80,7 +80,7 @@ function writeCSS(css) {
|
||||
const style = doc.createElement('style');
|
||||
style.type = 'text/css';
|
||||
style.textContent = css;
|
||||
doc.head.appendChild(style);
|
||||
doc.head.append(style);
|
||||
}
|
||||
|
||||
function loadScript(src) {
|
||||
@@ -93,8 +93,8 @@ function loadScript(src) {
|
||||
script.onerror = () => reject(new Error(src));
|
||||
script.src = src;
|
||||
// script.type = 'text/javascript';
|
||||
doc.head.appendChild(script);
|
||||
// doc.body.appendChild(element);
|
||||
doc.head.append(script);
|
||||
// doc.body.append(element);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<form class="wrapper submitting-pane loginForm thm-login thm-login-text" action="#/" data-bind="submit: submitForm, css: {'errorAnimated': formError, 'submitting': submitRequest()}">
|
||||
<div class="controls" data-bind="css: {'error': loginError, 'animated': loginErrorAnimation}">
|
||||
<div class="input-append">
|
||||
<input type="text" class="input-block-level inputLogin checkAutocomplete"
|
||||
<input type="text" class="input-block-level inputLogin"
|
||||
name="RainLoopAdminLogin" id="RainLoopAdminLogin"
|
||||
autofocus=""
|
||||
autocomplete="username" autocorrect="off" autocapitalize="off" spellcheck="false" data-i18n="[placeholder]LOGIN/LABEL_LOGIN"
|
||||
@@ -21,7 +21,7 @@
|
||||
</div>
|
||||
<div class="controls" data-bind="css: {'error': passwordError, 'animated': passwordErrorAnimation}">
|
||||
<div class="input-append">
|
||||
<input type="password" class="input-block-level inputPassword checkAutocomplete"
|
||||
<input type="password" class="input-block-level inputPassword"
|
||||
placeholder="Password" name="RainLoopAdminPassword" id="RainLoopAdminPassword"
|
||||
autocomplete="current-password" autocorrect="off" autocapitalize="off" spellcheck="false" data-i18n="[placeholder]LOGIN/LABEL_PASSWORD"
|
||||
data-bind="textInput: password, disable: submitRequest" />
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
<input class="i18n" type="date" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
|
||||
data-bind="attr: {'placeholder': placeholder}, enable: enable, css: className" />
|
||||
<!-- ko if: labeled -->
|
||||
|
||||
<span class="i18n" data-bind="attr: {'data-i18n': label}"></span>
|
||||
|
||||
<!-- /ko -->
|
||||
<!-- ko if: triggered -->
|
||||
|
||||
<div data-bind="component: {
|
||||
name: 'SaveTrigger',
|
||||
params: { value: trigger }
|
||||
}"></div>
|
||||
<!-- /ko -->
|
||||
@@ -1,7 +0,0 @@
|
||||
<div class="e-component e-radio" data-bind="foreach: values">
|
||||
<label tabindex="0" data-bind="click: $parent.click, css: {'inline': $parent.inline}">
|
||||
<i role="radio" data-bind="css: $parent.value() === value ? 'icon-radio-checked' : 'icon-radio-unchecked'"></i>
|
||||
|
||||
<span class="sub-label i18n" data-bind="attr: {'data-i18n': label}"></span>
|
||||
</label>
|
||||
</div>
|
||||
@@ -31,7 +31,7 @@
|
||||
<div class="controls plugin-mark-Login-TopControlGroup"
|
||||
data-bind="css: {'error': emailError, 'animated': emailErrorAnimation}">
|
||||
<div class="input-append">
|
||||
<input type="email" class="i18n input-block-level inputEmail checkAutocomplete"
|
||||
<input type="email" class="i18n input-block-level inputEmail"
|
||||
name="RainLoopEmail" id="RainLoopEmail"
|
||||
autofocus=""
|
||||
autocomplete="email" autocorrect="off" autocapitalize="off" spellcheck="false"
|
||||
@@ -44,7 +44,7 @@
|
||||
</div>
|
||||
<div class="controls" data-bind="css: {'error': passwordError, 'animated': passwordErrorAnimation}">
|
||||
<div class="input-append">
|
||||
<input type="password" class="i18n input-block-level inputPassword checkAutocomplete"
|
||||
<input type="password" class="i18n input-block-level inputPassword"
|
||||
name="RainLoopPassword" id="RainLoopPassword"
|
||||
autocomplete="current-password" autocorrect="off" autocapitalize="off" spellcheck="false"
|
||||
data-bind="textInput: password, disable: submitRequest"
|
||||
|
||||
4
vendors/inputosaurus/inputosaurus.js
vendored
4
vendors/inputosaurus/inputosaurus.js
vendored
@@ -6,8 +6,8 @@
|
||||
*
|
||||
* @requires:
|
||||
*
|
||||
* jQuery 1.7+
|
||||
* jQueryUI 1.8+ Core
|
||||
* jQuery 3.5+
|
||||
* jQueryUI 1.12+ Core
|
||||
*
|
||||
* @version 0.1.6
|
||||
* @author Dan Kielp <dan@sproutsocial.com>
|
||||
|
||||
Reference in New Issue
Block a user