Use less jQuery, more native

This commit is contained in:
djmaze
2020-08-27 15:45:47 +02:00
parent 24cb874c87
commit bdb36ec128
35 changed files with 492 additions and 779 deletions

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 {?}

View File

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

View File

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

View File

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

View File

@@ -3,8 +3,7 @@ import { Mime } from 'Common/Mime';
const
doc = document,
$ = jQuery,
$div = $('<div></div>'),
tpl = doc.createElement('template'),
isArray = Array.isArray,
htmlmap = {
'&': '&amp;',
@@ -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>&nbsp;' +
'<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(/&quot;/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(/&gt;/gi, '>')
.replace(/&lt;/gi, '<')
.replace(/&amp;/gi, '&');
text = splitPlainText(text);
.replace(/&amp;/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, '&amp;')
.replace(/>/g, '&gt;')
@@ -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}

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -128,7 +128,7 @@ class MailBoxUserScreen extends AbstractScreen {
, 1);
}
$html.on('click', '#rl-right', () => {
jQuery($html).on('click', '#rl-right', () => {
moveAction(false);
});
}

View File

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

View File

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

View File

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

View File

@@ -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 {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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>&nbsp;' +
'<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', () => {

View File

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

View File

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

View File

@@ -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" />

View File

@@ -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 -->
&nbsp;
<span class="i18n" data-bind="attr: {'data-i18n': label}"></span>
&nbsp;
<!-- /ko -->
<!-- ko if: triggered -->
&nbsp;
<div data-bind="component: {
name: 'SaveTrigger',
params: { value: trigger }
}"></div>
<!-- /ko -->

View File

@@ -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>
&nbsp;&nbsp;
<span class="sub-label i18n" data-bind="attr: {'data-i18n': label}"></span>
</label>
</div>

View File

@@ -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"

View File

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