From 1d6a6364334ee584ec66e8c9a726a851063600fe Mon Sep 17 00:00:00 2001 From: djmaze Date: Wed, 14 Oct 2020 19:16:37 +0200 Subject: [PATCH] Split Common/Utils.js Because admin app doesn't need most of them --- dev/App/User.js | 9 +- dev/Common/Utils.js | 560 +----------------- dev/Common/UtilsUser.js | 535 +++++++++++++++++ dev/Component/Select.js | 4 +- dev/Model/Email.js | 2 +- dev/Model/Filter.js | 15 +- dev/Model/Folder.js | 2 +- dev/Model/Message.js | 6 +- dev/Settings/Admin/Contacts.js | 4 +- dev/Settings/User/Filters.js | 2 +- dev/Settings/User/OpenPgp.js | 2 +- dev/Stores/User/Folder.js | 2 +- dev/Stores/User/Message.js | 7 +- dev/View/Popup/Compose.js | 8 +- dev/View/Popup/ComposeOpenPgp.js | 6 +- dev/View/Popup/Contacts.js | 7 +- dev/View/Popup/Filter.js | 4 +- dev/View/Popup/FolderCreate.js | 5 +- dev/View/Popup/FolderSystem.js | 5 +- dev/View/User/MailBox/MessageList.js | 2 +- dev/View/User/MailBox/MessageView.js | 7 +- .../templates/Views/Components/Select.html | 4 +- .../Views/User/PopupsFolderCreate.html | 2 +- .../Views/User/PopupsFolderSystem.html | 10 +- .../SettingsFiltersActionMoveToFolder.html | 2 +- 25 files changed, 592 insertions(+), 620 deletions(-) create mode 100644 dev/Common/UtilsUser.js diff --git a/dev/App/User.js b/dev/App/User.js index db5a9e116..cd2aa9422 100644 --- a/dev/App/User.js +++ b/dev/App/User.js @@ -1,10 +1,5 @@ -import { - isPosNumeric, - pInt, - pString, - delegateRunOnDestroy, - mailToHelper -} from 'Common/Utils'; +import { pInt, pString } from 'Common/Utils'; +import { isPosNumeric, delegateRunOnDestroy, mailToHelper } from 'Common/UtilsUser'; import { Layout, diff --git a/dev/Common/Utils.js b/dev/Common/Utils.js index e2f948818..a50ff1083 100644 --- a/dev/Common/Utils.js +++ b/dev/Common/Utils.js @@ -1,26 +1,7 @@ -import { ComposeType, SaveSettingsStep, FolderType } from 'Common/Enums'; +import { SaveSettingsStep } from 'Common/Enums'; const - doc = document, - tpl = doc.createElement('template'), - isArray = Array.isArray, - htmlmap = { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - "'": ''' - }, - htmlspecialchars = str => (''+str).replace(/[&<>"']/g, m => htmlmap[m]); - -/** - * @param {(string|number)} value - * @param {boolean=} includeZero = true - * @returns {boolean} - */ -export function isPosNumeric(value, includeZero = true) { - return null != value && (includeZero ? /^[0-9]*$/ : /^[1-9]+[0-9]*$/).test(value.toString()); -} + doc = document; /** * @param {*} value @@ -40,63 +21,17 @@ export function pString(value) { return null != value ? '' + value : ''; } -/** - * @param {string} text - * @returns {string} - */ -export function encodeHtml(text) { - return null != text ? htmlspecialchars(text.toString()) : ''; -} - -/** - * @param {string} text - * @param {number=} len = 100 - * @returns {string} - */ -function splitPlainText(text, len = 100) { - let prefix = '', - subText = '', - result = text, - spacePos = 0, - newLinePos = 0; - - while (result.length > len) { - subText = result.substr(0, len); - spacePos = subText.lastIndexOf(' '); - newLinePos = subText.lastIndexOf('\n'); - - if (-1 !== newLinePos) { - spacePos = newLinePos; - } - - if (-1 === spacePos) { - spacePos = len; - } - - prefix += subText.substr(0, spacePos) + '\n'; - result = result.substr(spacePos + 1); - } - - return prefix + result; -} - /** * @returns {boolean} */ export function inFocus() { try { - if (doc.activeElement) { - if (undefined === doc.activeElement.__inFocusCache) { - doc.activeElement.__inFocusCache = doc.activeElement.matches( - 'input,textarea,iframe,.cke_editable' - ); - } - - return !!doc.activeElement.__inFocusCache; - } - } catch (e) {} // eslint-disable-line no-empty - - return false; + return doc.activeElement && doc.activeElement.matches( + 'input,textarea,iframe,.cke_editable' + ); + } catch (e) { + return false; + } } /** @@ -109,9 +44,8 @@ export const convertThemeName = theme => { } return theme - .replace(/[^a-zA-Z0-9]+/g, ' ') .replace(/([A-Z])/g, ' $1') - .replace(/\s+/g, ' ') + .replace(/[^a-zA-Z0-9]+/g, ' ') .trim(); }; @@ -134,7 +68,7 @@ export function convertLangName(language, isEng = false) { * @param {object} item * @returns {void} */ -export function defautOptionsAfterRender(domItem, item) { +export function defaultOptionsAfterRender(domItem, item) { if (item && undefined !== item.disabled && domItem) { domItem.classList.toggle('disabled', domItem.disabled = item.disabled); } @@ -152,306 +86,6 @@ export function settingsSaveHelperSimpleFunction(koTrigger, context) { }; } -/** - * @param {string} html - * @returns {string} - */ -export function htmlToPlain(html) { - let pos = 0, - limit = 0, - iP1 = 0, - iP2 = 0, - iP3 = 0, - text = ''; - - const convertBlockquote = (blockquoteText) => { - blockquoteText = '> ' + blockquoteText.trim().replace(/\n/gm, '\n> '); - return blockquoteText.replace(/(^|\n)([> ]+)/gm, (...args) => - args && 2 < args.length ? args[1] + args[2].replace(/[\s]/g, '').trim() + ' ' : '' - ); - }; - - const convertDivs = (...args) => { - if (args && 1 < args.length) { - let divText = args[1].trim(); - if (divText.length) { - divText = divText.replace(/]*>([\s\S\r\n]*)<\/div>/gim, convertDivs); - divText = '\n' + divText.trim() + '\n'; - } - - return divText; - } - - return ''; - }; - - const convertPre = (...args) => - args && 1 < args.length - ? args[1] - .toString() - .replace(/[\n]/gm, '
') - .replace(/[\r]/gm, '') - : '', - fixAttibuteValue = (...args) => (args && 1 < args.length ? '' + args[1] + htmlspecialchars(args[2]) : ''), - convertLinks = (...args) => (args && 1 < args.length ? args[1].trim() : ''); - - tpl.innerHTML = html - .replace(/]*><\/p>/gi, '') - .replace(/]*>([\s\S\r\n\t]*)<\/pre>/gim, convertPre) - .replace(/[\s]+/gm, ' ') - .replace(/((?:href|data)\s?=\s?)("[^"]+?"|'[^']+?')/gim, fixAttibuteValue) - .replace(/]*>/gim, '\n') - .replace(/<\/h[\d]>/gi, '\n') - .replace(/<\/p>/gi, '\n\n') - .replace(/]*>/gim, '\n') - .replace(/<\/ul>/gi, '\n') - .replace(/]*>/gim, ' * ') - .replace(/<\/li>/gi, '\n') - .replace(/<\/td>/gi, '\n') - .replace(/<\/tr>/gi, '\n') - .replace(/]*>/gim, '\n_______________________________\n\n') - .replace(/]*>([\s\S\r\n]*)<\/div>/gim, convertDivs) - .replace(/]*>/gim, '\n__bq__start__\n') - .replace(/<\/blockquote>/gim, '\n__bq__end__\n') - .replace(/]*>([\s\S\r\n]*?)<\/a>/gim, convertLinks) - .replace(/<\/div>/gi, '\n') - .replace(/ /gi, ' ') - .replace(/"/gi, '"') - .replace(/<[^>]*>/gm, ''); - - text = splitPlainText(tpl.innerText - .replace(/\n[ \t]+/gm, '\n') - .replace(/[\n]{3,}/gm, '\n\n') - .replace(/>/gi, '>') - .replace(/</gi, '<') - .replace(/&/gi, '&') - ); - - pos = 0; - limit = 800; - - while (0 < limit) { - --limit; - iP1 = text.indexOf('__bq__start__', pos); - if (-1 < iP1) { - iP2 = text.indexOf('__bq__start__', iP1 + 5); - iP3 = text.indexOf('__bq__end__', iP1 + 5); - - if ((-1 === iP2 || iP3 < iP2) && iP1 < iP3) { - text = text.substr(0, iP1) + convertBlockquote(text.substring(iP1 + 13, iP3)) + text.substr(iP3 + 11); - pos = 0; - } else if (-1 < iP2 && iP2 < iP3) { - pos = iP2 - 1; - } else { - pos = 0; - } - } else { - break; - } - } - - return text.replace(/__bq__start__|__bq__end__/gm, '').trim(); -} - -/** - * @param {string} plain - * @param {boolean} findEmailAndLinksInText = false - * @returns {string} - */ -export function plainToHtml(plain) { - plain = plain.toString().replace(/\r/g, ''); - plain = plain.replace(/^>[> ]>+/gm, ([match]) => (match ? match.replace(/[ ]+/g, '') : match)); - - let bIn = false, - bDo = true, - bStart = true, - aNextText = [], - aText = plain.split('\n'); - - do { - bDo = false; - aNextText = []; - aText.forEach(sLine => { - bStart = '>' === sLine.substr(0, 1); - if (bStart && !bIn) { - bDo = true; - bIn = true; - aNextText.push('~~~blockquote~~~'); - aNextText.push(sLine.substr(1)); - } else if (!bStart && bIn) { - if (sLine) { - bIn = false; - aNextText.push('~~~/blockquote~~~'); - aNextText.push(sLine); - } else { - aNextText.push(sLine); - } - } else if (bStart && bIn) { - aNextText.push(sLine.substr(1)); - } else { - aNextText.push(sLine); - } - }); - - if (bIn) { - bIn = false; - aNextText.push('~~~/blockquote~~~'); - } - - aText = aNextText; - } while (bDo); - - return aText.join('\n') - // .replace(/~~~\/blockquote~~~\n~~~blockquote~~~/g, '\n') - .replace(/&/g, '&') - .replace(/>/g, '>') - .replace(/') - .replace(/[\s]*~~~\/blockquote~~~/g, '') - .replace(/\n/g, '
'); -} - -rl.Utils = { - htmlToPlain: htmlToPlain, - plainToHtml: plainToHtml -}; - -/** - * @param {Array} aSystem - * @param {Array} aList - * @param {Array=} aDisabled - * @param {Array=} aHeaderLines - * @param {?number=} iUnDeep - * @param {Function=} fDisableCallback - * @param {Function=} fVisibleCallback - * @param {Function=} fRenameCallback - * @param {boolean=} bSystem - * @param {boolean=} bBuildUnvisible - * @returns {Array} - */ -export function folderListOptionsBuilder( - aSystem, - aList, - aDisabled, - aHeaderLines, - iUnDeep, - fDisableCallback, - fVisibleCallback, - fRenameCallback, - bSystem, - bBuildUnvisible -) { - let /** - * @type {?FolderModel} - */ - bSep = false, - aResult = []; - - const sDeepPrefix = '\u00A0\u00A0\u00A0'; - - bBuildUnvisible = undefined === bBuildUnvisible ? false : !!bBuildUnvisible; - bSystem = null == bSystem ? 0 < aSystem.length : bSystem; - iUnDeep = null == iUnDeep ? 0 : iUnDeep; - fDisableCallback = null != fDisableCallback ? fDisableCallback : null; - fVisibleCallback = null != fVisibleCallback ? fVisibleCallback : null; - fRenameCallback = null != fRenameCallback ? fRenameCallback : null; - - if (!isArray(aDisabled)) { - aDisabled = []; - } - - if (!isArray(aHeaderLines)) { - aHeaderLines = []; - } - - aHeaderLines.forEach(line => { - aResult.push({ - id: line[0], - name: line[1], - system: false, - dividerbar: false, - disabled: false - }); - }); - - bSep = true; - aSystem.forEach(oItem => { - if (fVisibleCallback ? fVisibleCallback(oItem) : true) { - aResult.push({ - id: oItem.fullNameRaw, - name: fRenameCallback ? fRenameCallback(oItem) : oItem.name(), - system: true, - dividerbar: bSep, - disabled: - !oItem.selectable || - aDisabled.includes(oItem.fullNameRaw) || - (fDisableCallback ? fDisableCallback(oItem) : false) - }); - bSep = false; - } - }); - - bSep = true; - aList.forEach(oItem => { - // if (oItem.subScribed() || !oItem.existen || bBuildUnvisible) - if ( - (oItem.subScribed() || !oItem.existen || bBuildUnvisible) && - (oItem.selectable || oItem.hasSubScribedSubfolders()) - ) { - if (fVisibleCallback ? fVisibleCallback(oItem) : true) { - if (FolderType.User === oItem.type() || !bSystem || oItem.hasSubScribedSubfolders()) { - aResult.push({ - id: oItem.fullNameRaw, - name: - new Array(oItem.deep + 1 - iUnDeep).join(sDeepPrefix) + - (fRenameCallback ? fRenameCallback(oItem) : oItem.name()), - system: false, - dividerbar: bSep, - disabled: - !oItem.selectable || - aDisabled.includes(oItem.fullNameRaw) || - (fDisableCallback ? fDisableCallback(oItem) : false) - }); - bSep = false; - } - } - } - - if (oItem.subScribed() && oItem.subFolders().length) { - aResult = aResult.concat( - folderListOptionsBuilder( - [], - oItem.subFolders(), - aDisabled, - [], - iUnDeep, - fDisableCallback, - fVisibleCallback, - fRenameCallback, - bSystem, - bBuildUnvisible - ) - ); - } - }); - - return aResult; -} - -/** - * @param {Object|Array} objectOrObjects - * @returns {void} - */ -export function delegateRunOnDestroy(objectOrObjects) { - if (objectOrObjects) { - if (isArray(objectOrObjects)) { - objectOrObjects.forEach(item => delegateRunOnDestroy(item)); - } else { - objectOrObjects.onDestroy && objectOrObjects.onDestroy(); - } - } -} - let __themeTimer = 0, __themeAjax = null; @@ -494,7 +128,7 @@ export function changeTheme(value, themeTrigger = ()=>{}) { } rl.fetchJSON(url, init) .then(data => { - if (data && isArray(data) && 2 === data.length) { + if (data && Array.isArray(data) && 2 === data.length) { if (themeLink && !themeStyle) { themeStyle = doc.createElement('style'); themeStyle.id = 'app-theme-style'; @@ -514,175 +148,3 @@ export function changeTheme(value, themeTrigger = ()=>{}) { .then(clearTimer, clearTimer); } } - -/** - * @returns {function} - */ -export function computedPaginatorHelper(koCurrentPage, koPageCount) { - return () => { - const currentPage = koCurrentPage(), - pageCount = koPageCount(), - result = [], - fAdd = (index, push = true, customName = '') => { - const data = { - current: index === currentPage, - name: customName ? customName.toString() : index.toString(), - custom: !!customName, - title: customName ? index.toString() : '', - value: index.toString() - }; - - if (push) { - result.push(data); - } else { - result.unshift(data); - } - }; - - let prev = 0, - next = 0, - limit = 2; - - if (1 < pageCount || (0 < pageCount && pageCount < currentPage)) { - if (pageCount < currentPage) { - fAdd(pageCount); - prev = pageCount; - next = pageCount; - } else { - if (3 >= currentPage || pageCount - 2 <= currentPage) { - limit += 2; - } - - fAdd(currentPage); - prev = currentPage; - next = currentPage; - } - - while (0 < limit) { - --prev; - ++next; - - if (0 < prev) { - fAdd(prev, false); - --limit; - } - - if (pageCount >= next) { - fAdd(next, true); - --limit; - } else if (0 >= prev) { - break; - } - } - - if (3 === prev) { - fAdd(2, false); - } else if (3 < prev) { - fAdd(Math.round((prev - 1) / 2), false, '...'); - } - - if (pageCount - 2 === next) { - fAdd(pageCount - 1, true); - } else if (pageCount - 2 > next) { - fAdd(Math.round((pageCount + next) / 2), true, '...'); - } - - // first and last - if (1 < prev) { - fAdd(1, false); - } - - if (pageCount > next) { - fAdd(pageCount, true); - } - } - - return result; - }; -} - -/** - * @param {string} color - * @returns {boolean} - */ -export function isTransparent(color) { - return 'rgba(0, 0, 0, 0)' === color || 'transparent' === color; -} - -/** - * @param {string} mailToUrl - * @param {Function} PopupComposeViewModel - * @returns {boolean} - */ -export function mailToHelper(mailToUrl, PopupComposeViewModel) { - if ( - mailToUrl && - 'mailto:' === - mailToUrl - .toString() - .substr(0, 7) - .toLowerCase() - ) { - if (!PopupComposeViewModel) { - return true; - } - - mailToUrl = mailToUrl.toString().substr(7); - - let to = [], - cc = null, - bcc = null, - params = {}; - - const email = mailToUrl.replace(/\?.+$/, ''), - query = mailToUrl.replace(/^[^?]*\?/, ''), - EmailModel = require('Model/Email').default; - - query.split('&').forEach(temp => { - temp = temp.split('='); - params[decodeURIComponent(temp[0])] = decodeURIComponent(temp[1]); - }); - - if (undefined !== params.to) { - to = EmailModel.parseEmailLine(decodeURIComponent(email + ',' + params.to)); - to = Object.values( - to.reduce((result, value) => { - if (value) { - if (result[value.email]) { - if (!result[value.email].name) { - result[value.email] = value; - } - } else { - result[value.email] = value; - } - } - return result; - }, {}) - ); - } else { - to = EmailModel.parseEmailLine(email); - } - - if (undefined !== params.cc) { - cc = EmailModel.parseEmailLine(decodeURIComponent(params.cc)); - } - - if (undefined !== params.bcc) { - bcc = EmailModel.parseEmailLine(decodeURIComponent(params.bcc)); - } - - require('Knoin/Knoin').showScreenPopup(PopupComposeViewModel, [ - ComposeType.Empty, - null, - to, - cc, - bcc, - null == params.subject ? null : pString(decodeURIComponent(params.subject)), - null == params.body ? null : plainToHtml(pString(decodeURIComponent(params.body))) - ]); - - return true; - } - - return false; -} diff --git a/dev/Common/UtilsUser.js b/dev/Common/UtilsUser.js new file mode 100644 index 000000000..f9e896eef --- /dev/null +++ b/dev/Common/UtilsUser.js @@ -0,0 +1,535 @@ +import { ComposeType, FolderType } from 'Common/Enums'; +import { pString } from 'Common/Utils'; + +const + tpl = document.createElement('template'), + isArray = Array.isArray, + htmlmap = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + }, + htmlspecialchars = str => (''+str).replace(/[&<>"']/g, m => htmlmap[m]); + +/** + * @param {(string|number)} value + * @param {boolean=} includeZero = true + * @returns {boolean} + */ +export function isPosNumeric(value, includeZero = true) { + return null != value && (includeZero ? /^[0-9]*$/ : /^[1-9]+[0-9]*$/).test(value.toString()); +} + +/** + * @param {string} text + * @returns {string} + */ +export function encodeHtml(text) { + return null != text ? htmlspecialchars(text.toString()) : ''; +} + +/** + * @param {string} text + * @param {number=} len = 100 + * @returns {string} + */ +function splitPlainText(text, len = 100) { + let prefix = '', + subText = '', + result = text, + spacePos = 0, + newLinePos = 0; + + while (result.length > len) { + subText = result.substr(0, len); + spacePos = subText.lastIndexOf(' '); + newLinePos = subText.lastIndexOf('\n'); + + if (-1 !== newLinePos) { + spacePos = newLinePos; + } + + if (-1 === spacePos) { + spacePos = len; + } + + prefix += subText.substr(0, spacePos) + '\n'; + result = result.substr(spacePos + 1); + } + + return prefix + result; +} + +/** + * @param {string} html + * @returns {string} + */ +export function htmlToPlain(html) { + let pos = 0, + limit = 0, + iP1 = 0, + iP2 = 0, + iP3 = 0, + text = ''; + + const convertBlockquote = (blockquoteText) => { + blockquoteText = '> ' + blockquoteText.trim().replace(/\n/gm, '\n> '); + return blockquoteText.replace(/(^|\n)([> ]+)/gm, (...args) => + args && 2 < args.length ? args[1] + args[2].replace(/[\s]/g, '').trim() + ' ' : '' + ); + }; + + const convertDivs = (...args) => { + if (args && 1 < args.length) { + let divText = args[1].trim(); + if (divText.length) { + divText = divText.replace(/]*>([\s\S\r\n]*)<\/div>/gim, convertDivs); + divText = '\n' + divText.trim() + '\n'; + } + + return divText; + } + + return ''; + }; + + const convertPre = (...args) => + args && 1 < args.length + ? args[1] + .toString() + .replace(/[\n]/gm, '
') + .replace(/[\r]/gm, '') + : '', + fixAttibuteValue = (...args) => (args && 1 < args.length ? '' + args[1] + htmlspecialchars(args[2]) : ''), + convertLinks = (...args) => (args && 1 < args.length ? args[1].trim() : ''); + + tpl.innerHTML = html + .replace(/]*><\/p>/gi, '') + .replace(/]*>([\s\S\r\n\t]*)<\/pre>/gim, convertPre) + .replace(/[\s]+/gm, ' ') + .replace(/((?:href|data)\s?=\s?)("[^"]+?"|'[^']+?')/gim, fixAttibuteValue) + .replace(/]*>/gim, '\n') + .replace(/<\/h[\d]>/gi, '\n') + .replace(/<\/p>/gi, '\n\n') + .replace(/]*>/gim, '\n') + .replace(/<\/ul>/gi, '\n') + .replace(/]*>/gim, ' * ') + .replace(/<\/li>/gi, '\n') + .replace(/<\/td>/gi, '\n') + .replace(/<\/tr>/gi, '\n') + .replace(/]*>/gim, '\n_______________________________\n\n') + .replace(/]*>([\s\S\r\n]*)<\/div>/gim, convertDivs) + .replace(/]*>/gim, '\n__bq__start__\n') + .replace(/<\/blockquote>/gim, '\n__bq__end__\n') + .replace(/
]*>([\s\S\r\n]*?)<\/a>/gim, convertLinks) + .replace(/<\/div>/gi, '\n') + .replace(/ /gi, ' ') + .replace(/"/gi, '"') + .replace(/<[^>]*>/gm, ''); + + text = splitPlainText(tpl.innerText + .replace(/\n[ \t]+/gm, '\n') + .replace(/[\n]{3,}/gm, '\n\n') + .replace(/>/gi, '>') + .replace(/</gi, '<') + .replace(/&/gi, '&') + ); + + pos = 0; + limit = 800; + + while (0 < limit) { + --limit; + iP1 = text.indexOf('__bq__start__', pos); + if (-1 < iP1) { + iP2 = text.indexOf('__bq__start__', iP1 + 5); + iP3 = text.indexOf('__bq__end__', iP1 + 5); + + if ((-1 === iP2 || iP3 < iP2) && iP1 < iP3) { + text = text.substr(0, iP1) + convertBlockquote(text.substring(iP1 + 13, iP3)) + text.substr(iP3 + 11); + pos = 0; + } else if (-1 < iP2 && iP2 < iP3) { + pos = iP2 - 1; + } else { + pos = 0; + } + } else { + break; + } + } + + return text.replace(/__bq__start__|__bq__end__/gm, '').trim(); +} + +/** + * @param {string} plain + * @param {boolean} findEmailAndLinksInText = false + * @returns {string} + */ +export function plainToHtml(plain) { + plain = plain.toString().replace(/\r/g, ''); + plain = plain.replace(/^>[> ]>+/gm, ([match]) => (match ? match.replace(/[ ]+/g, '') : match)); + + let bIn = false, + bDo = true, + bStart = true, + aNextText = [], + aText = plain.split('\n'); + + do { + bDo = false; + aNextText = []; + aText.forEach(sLine => { + bStart = '>' === sLine.substr(0, 1); + if (bStart && !bIn) { + bDo = true; + bIn = true; + aNextText.push('~~~blockquote~~~'); + aNextText.push(sLine.substr(1)); + } else if (!bStart && bIn) { + if (sLine) { + bIn = false; + aNextText.push('~~~/blockquote~~~'); + aNextText.push(sLine); + } else { + aNextText.push(sLine); + } + } else if (bStart && bIn) { + aNextText.push(sLine.substr(1)); + } else { + aNextText.push(sLine); + } + }); + + if (bIn) { + bIn = false; + aNextText.push('~~~/blockquote~~~'); + } + + aText = aNextText; + } while (bDo); + + return aText.join('\n') + // .replace(/~~~\/blockquote~~~\n~~~blockquote~~~/g, '\n') + .replace(/&/g, '&') + .replace(/>/g, '>') + .replace(/') + .replace(/[\s]*~~~\/blockquote~~~/g, '') + .replace(/\n/g, '
'); +} + +rl.Utils = { + htmlToPlain: htmlToPlain, + plainToHtml: plainToHtml +}; + +/** + * @param {Array} aSystem + * @param {Array} aList + * @param {Array=} aDisabled + * @param {Array=} aHeaderLines + * @param {?number=} iUnDeep + * @param {Function=} fDisableCallback + * @param {Function=} fVisibleCallback + * @param {Function=} fRenameCallback + * @param {boolean=} bSystem + * @param {boolean=} bBuildUnvisible + * @returns {Array} + */ +export function folderListOptionsBuilder( + aSystem, + aList, + aDisabled, + aHeaderLines, + iUnDeep, + fDisableCallback, + fVisibleCallback, + fRenameCallback, + bSystem, + bBuildUnvisible +) { + let /** + * @type {?FolderModel} + */ + bSep = false, + aResult = []; + + const sDeepPrefix = '\u00A0\u00A0\u00A0'; + + bBuildUnvisible = undefined === bBuildUnvisible ? false : !!bBuildUnvisible; + bSystem = null == bSystem ? 0 < aSystem.length : bSystem; + iUnDeep = null == iUnDeep ? 0 : iUnDeep; + fDisableCallback = null != fDisableCallback ? fDisableCallback : null; + fVisibleCallback = null != fVisibleCallback ? fVisibleCallback : null; + fRenameCallback = null != fRenameCallback ? fRenameCallback : null; + + if (!isArray(aDisabled)) { + aDisabled = []; + } + + if (!isArray(aHeaderLines)) { + aHeaderLines = []; + } + + aHeaderLines.forEach(line => { + aResult.push({ + id: line[0], + name: line[1], + system: false, + dividerbar: false, + disabled: false + }); + }); + + bSep = true; + aSystem.forEach(oItem => { + if (fVisibleCallback ? fVisibleCallback(oItem) : true) { + aResult.push({ + id: oItem.fullNameRaw, + name: fRenameCallback ? fRenameCallback(oItem) : oItem.name(), + system: true, + dividerbar: bSep, + disabled: + !oItem.selectable || + aDisabled.includes(oItem.fullNameRaw) || + (fDisableCallback ? fDisableCallback(oItem) : false) + }); + bSep = false; + } + }); + + bSep = true; + aList.forEach(oItem => { + // if (oItem.subScribed() || !oItem.existen || bBuildUnvisible) + if ( + (oItem.subScribed() || !oItem.existen || bBuildUnvisible) && + (oItem.selectable || oItem.hasSubScribedSubfolders()) + ) { + if (fVisibleCallback ? fVisibleCallback(oItem) : true) { + if (FolderType.User === oItem.type() || !bSystem || oItem.hasSubScribedSubfolders()) { + aResult.push({ + id: oItem.fullNameRaw, + name: + new Array(oItem.deep + 1 - iUnDeep).join(sDeepPrefix) + + (fRenameCallback ? fRenameCallback(oItem) : oItem.name()), + system: false, + dividerbar: bSep, + disabled: + !oItem.selectable || + aDisabled.includes(oItem.fullNameRaw) || + (fDisableCallback ? fDisableCallback(oItem) : false) + }); + bSep = false; + } + } + } + + if (oItem.subScribed() && oItem.subFolders().length) { + aResult = aResult.concat( + folderListOptionsBuilder( + [], + oItem.subFolders(), + aDisabled, + [], + iUnDeep, + fDisableCallback, + fVisibleCallback, + fRenameCallback, + bSystem, + bBuildUnvisible + ) + ); + } + }); + + return aResult; +} + +/** + * @param {Object|Array} objectOrObjects + * @returns {void} + */ +export function delegateRunOnDestroy(objectOrObjects) { + if (objectOrObjects) { + if (isArray(objectOrObjects)) { + objectOrObjects.forEach(item => delegateRunOnDestroy(item)); + } else { + objectOrObjects.onDestroy && objectOrObjects.onDestroy(); + } + } +} + +/** + * @returns {function} + */ +export function computedPaginatorHelper(koCurrentPage, koPageCount) { + return () => { + const currentPage = koCurrentPage(), + pageCount = koPageCount(), + result = [], + fAdd = (index, push = true, customName = '') => { + const data = { + current: index === currentPage, + name: customName ? customName.toString() : index.toString(), + custom: !!customName, + title: customName ? index.toString() : '', + value: index.toString() + }; + + if (push) { + result.push(data); + } else { + result.unshift(data); + } + }; + + let prev = 0, + next = 0, + limit = 2; + + if (1 < pageCount || (0 < pageCount && pageCount < currentPage)) { + if (pageCount < currentPage) { + fAdd(pageCount); + prev = pageCount; + next = pageCount; + } else { + if (3 >= currentPage || pageCount - 2 <= currentPage) { + limit += 2; + } + + fAdd(currentPage); + prev = currentPage; + next = currentPage; + } + + while (0 < limit) { + --prev; + ++next; + + if (0 < prev) { + fAdd(prev, false); + --limit; + } + + if (pageCount >= next) { + fAdd(next, true); + --limit; + } else if (0 >= prev) { + break; + } + } + + if (3 === prev) { + fAdd(2, false); + } else if (3 < prev) { + fAdd(Math.round((prev - 1) / 2), false, '...'); + } + + if (pageCount - 2 === next) { + fAdd(pageCount - 1, true); + } else if (pageCount - 2 > next) { + fAdd(Math.round((pageCount + next) / 2), true, '...'); + } + + // first and last + if (1 < prev) { + fAdd(1, false); + } + + if (pageCount > next) { + fAdd(pageCount, true); + } + } + + return result; + }; +} + +/** + * @param {string} color + * @returns {boolean} + */ +export function isTransparent(color) { + return 'rgba(0, 0, 0, 0)' === color || 'transparent' === color; +} + +/** + * @param {string} mailToUrl + * @param {Function} PopupComposeViewModel + * @returns {boolean} + */ +export function mailToHelper(mailToUrl, PopupComposeViewModel) { + if ( + mailToUrl && + 'mailto:' === + mailToUrl + .toString() + .substr(0, 7) + .toLowerCase() + ) { + if (!PopupComposeViewModel) { + return true; + } + + mailToUrl = mailToUrl.toString().substr(7); + + let to = [], + cc = null, + bcc = null, + params = {}; + + const email = mailToUrl.replace(/\?.+$/, ''), + query = mailToUrl.replace(/^[^?]*\?/, ''), + EmailModel = require('Model/Email').default; + + query.split('&').forEach(temp => { + temp = temp.split('='); + params[decodeURIComponent(temp[0])] = decodeURIComponent(temp[1]); + }); + + if (undefined !== params.to) { + to = EmailModel.parseEmailLine(decodeURIComponent(email + ',' + params.to)); + to = Object.values( + to.reduce((result, value) => { + if (value) { + if (result[value.email]) { + if (!result[value.email].name) { + result[value.email] = value; + } + } else { + result[value.email] = value; + } + } + return result; + }, {}) + ); + } else { + to = EmailModel.parseEmailLine(email); + } + + if (undefined !== params.cc) { + cc = EmailModel.parseEmailLine(decodeURIComponent(params.cc)); + } + + if (undefined !== params.bcc) { + bcc = EmailModel.parseEmailLine(decodeURIComponent(params.bcc)); + } + + require('Knoin/Knoin').showScreenPopup(PopupComposeViewModel, [ + ComposeType.Empty, + null, + to, + cc, + bcc, + null == params.subject ? null : pString(decodeURIComponent(params.subject)), + null == params.body ? null : plainToHtml(pString(decodeURIComponent(params.body))) + ]); + + return true; + } + + return false; +} diff --git a/dev/Component/Select.js b/dev/Component/Select.js index 0e6836380..37205869d 100644 --- a/dev/Component/Select.js +++ b/dev/Component/Select.js @@ -1,5 +1,5 @@ import { i18n } from 'Common/Translator'; -import { defautOptionsAfterRender } from 'Common/Utils'; +import { defaultOptionsAfterRender } from 'Common/Utils'; import { componentExportHelper } from 'Component/Abstract'; import { AbstractInput } from 'Component/AbstractInput'; @@ -20,7 +20,7 @@ class SelectComponent extends AbstractInput { this.optionsCaption = i18n(this.optionsCaption); } - this.defautOptionsAfterRender = defautOptionsAfterRender; + this.defaultOptionsAfterRender = defaultOptionsAfterRender; } } diff --git a/dev/Model/Email.js b/dev/Model/Email.js index 8435164b9..9c8cdd5ad 100644 --- a/dev/Model/Email.js +++ b/dev/Model/Email.js @@ -1,4 +1,4 @@ -import { encodeHtml } from 'Common/Utils'; +import { encodeHtml } from 'Common/UtilsUser'; 'use strict'; diff --git a/dev/Model/Filter.js b/dev/Model/Filter.js index e2a3cee78..53c399ef1 100644 --- a/dev/Model/Filter.js +++ b/dev/Model/Filter.js @@ -1,7 +1,8 @@ import ko from 'ko'; import { FilterRulesType, FiltersAction } from 'Common/Enums'; -import { pString, delegateRunOnDestroy } from 'Common/Utils'; +import { pString } from 'Common/Utils'; +import { delegateRunOnDestroy } from 'Common/UtilsUser'; import { i18n } from 'Common/Translator'; import { getFolderFromCacheList } from 'Common/Cache'; @@ -114,17 +115,9 @@ class FilterModel extends AbstractModel { return result; }); - this.regDisposables( - this.name.subscribe((sValue) => { - this.name.error(!sValue); - }) - ); + this.regDisposables(this.name.subscribe(sValue => this.name.error(!sValue))); - this.regDisposables( - this.actionValue.subscribe((sValue) => { - this.actionValue.error(!sValue); - }) - ); + this.regDisposables(this.actionValue.subscribe(sValue => this.actionValue.error(!sValue))); this.regDisposables([this.actionNoStop, this.actionTemplate]); diff --git a/dev/Model/Folder.js b/dev/Model/Folder.js index ff1f09074..db0aa434d 100644 --- a/dev/Model/Folder.js +++ b/dev/Model/Folder.js @@ -1,7 +1,7 @@ import ko from 'ko'; import { FolderType } from 'Common/Enums'; -import { isPosNumeric } from 'Common/Utils'; +import { isPosNumeric } from 'Common/UtilsUser'; import { i18n, trigger as translatorTrigger } from 'Common/Translator'; import { getFolderInboxName } from 'Common/Cache'; diff --git a/dev/Model/Message.js b/dev/Model/Message.js index 928b5b84c..620a3e5d8 100644 --- a/dev/Model/Message.js +++ b/dev/Model/Message.js @@ -3,10 +3,8 @@ import ko from 'ko'; import { MessagePriority, SignedVerifyStatus } from 'Common/Enums'; import { i18n } from 'Common/Translator'; -import { - pInt, - encodeHtml -} from 'Common/Utils'; +import { pInt } from 'Common/Utils'; +import { encodeHtml } from 'Common/UtilsUser'; import { messageViewLink, messageDownloadLink } from 'Common/Links'; diff --git a/dev/Settings/Admin/Contacts.js b/dev/Settings/Admin/Contacts.js index 5c4ee2115..f45e03251 100644 --- a/dev/Settings/Admin/Contacts.js +++ b/dev/Settings/Admin/Contacts.js @@ -1,6 +1,6 @@ import ko from 'ko'; -import { settingsSaveHelperSimpleFunction, defautOptionsAfterRender } from 'Common/Utils'; +import { settingsSaveHelperSimpleFunction, defaultOptionsAfterRender } from 'Common/Utils'; import { SaveSettingsStep, StorageResultType } from 'Common/Enums'; import Remote from 'Remote/Admin/Fetch'; @@ -10,7 +10,7 @@ const settingsGet = rl.settings.get; class ContactsAdminSettings { constructor() { - this.defautOptionsAfterRender = defautOptionsAfterRender; + this.defaultOptionsAfterRender = defaultOptionsAfterRender; this.enableContacts = ko.observable(!!settingsGet('ContactsEnable')); this.contactsSync = ko.observable(!!settingsGet('ContactsSync')); diff --git a/dev/Settings/User/Filters.js b/dev/Settings/User/Filters.js index 54c4a0498..86df16741 100644 --- a/dev/Settings/User/Filters.js +++ b/dev/Settings/User/Filters.js @@ -1,6 +1,6 @@ import ko from 'ko'; -import { delegateRunOnDestroy } from 'Common/Utils'; +import { delegateRunOnDestroy } from 'Common/UtilsUser'; import { StorageResultType, Notification } from 'Common/Enums'; import { getNotification } from 'Common/Translator'; diff --git a/dev/Settings/User/OpenPgp.js b/dev/Settings/User/OpenPgp.js index f2dca4499..6293766ac 100644 --- a/dev/Settings/User/OpenPgp.js +++ b/dev/Settings/User/OpenPgp.js @@ -1,6 +1,6 @@ import ko from 'ko'; -import { delegateRunOnDestroy } from 'Common/Utils'; +import { delegateRunOnDestroy } from 'Common/UtilsUser'; import PgpStore from 'Stores/User/Pgp'; import SettingsStore from 'Stores/User/Settings'; diff --git a/dev/Stores/User/Folder.js b/dev/Stores/User/Folder.js index 59fdcbf9e..1d358d39d 100644 --- a/dev/Stores/User/Folder.js +++ b/dev/Stores/User/Folder.js @@ -2,7 +2,7 @@ import ko from 'ko'; import { FolderType } from 'Common/Enums'; import { UNUSED_OPTION_VALUE } from 'Common/Consts'; -import { folderListOptionsBuilder } from 'Common/Utils'; +import { folderListOptionsBuilder } from 'Common/UtilsUser'; import { getFolderInboxName, getFolderFromCacheList } from 'Common/Cache'; class FolderUserStore { diff --git a/dev/Stores/User/Message.js b/dev/Stores/User/Message.js index 17f57d1a0..1667a0ae1 100644 --- a/dev/Stores/User/Message.js +++ b/dev/Stores/User/Message.js @@ -2,11 +2,8 @@ import ko from 'ko'; import { Layout, Focused, MessageSetAction, StorageResultType, Notification } from 'Common/Enums'; -import { - pInt, - pString, - plainToHtml -} from 'Common/Utils'; +import { pInt, pString } from 'Common/Utils'; +import { plainToHtml } from 'Common/UtilsUser'; import { getFolderInboxName, diff --git a/dev/View/Popup/Compose.js b/dev/View/Popup/Compose.js index a6faea15e..514b30f7c 100644 --- a/dev/View/Popup/Compose.js +++ b/dev/View/Popup/Compose.js @@ -11,12 +11,8 @@ import { UploadErrorCode } from 'Common/Enums'; -import { - encodeHtml, - inFocus, - delegateRunOnDestroy, - pInt -} from 'Common/Utils'; +import { inFocus, pInt } from 'Common/Utils'; +import { encodeHtml, delegateRunOnDestroy } from 'Common/UtilsUser'; import { UNUSED_OPTION_VALUE } from 'Common/Consts'; import { upload } from 'Common/Links'; diff --git a/dev/View/Popup/ComposeOpenPgp.js b/dev/View/Popup/ComposeOpenPgp.js index 77158bb04..70984499a 100644 --- a/dev/View/Popup/ComposeOpenPgp.js +++ b/dev/View/Popup/ComposeOpenPgp.js @@ -1,6 +1,6 @@ import ko from 'ko'; -import { pString, defautOptionsAfterRender } from 'Common/Utils'; +import { pString, defaultOptionsAfterRender } from 'Common/Utils'; import { KeyState } from 'Common/Enums'; import { i18n } from 'Common/Translator'; @@ -93,10 +93,10 @@ class ComposeOpenPgpPopupView extends AbstractViewNext { this.sDefaultKeyScope = KeyState.PopupComposeOpenPGP; - this.defautOptionsAfterRender = defautOptionsAfterRender; + this.defaultOptionsAfterRender = defaultOptionsAfterRender; this.addOptionClass = (domOption, item) => { - this.defautOptionsAfterRender(domOption, item); + this.defaultOptionsAfterRender(domOption, item); if (item && undefined !== item.class && domOption) { domOption.classList.add(item.class); diff --git a/dev/View/Popup/Contacts.js b/dev/View/Popup/Contacts.js index f0244f4aa..6de440c8c 100644 --- a/dev/View/Popup/Contacts.js +++ b/dev/View/Popup/Contacts.js @@ -10,11 +10,8 @@ import { KeyState } from 'Common/Enums'; -import { - delegateRunOnDestroy, - computedPaginatorHelper, - pInt -} from 'Common/Utils'; +import { pInt } from 'Common/Utils'; +import { delegateRunOnDestroy, computedPaginatorHelper } from 'Common/UtilsUser'; import { Selector } from 'Common/Selector'; import { exportContactsVcf, exportContactsCsv, uploadContacts } from 'Common/Links'; diff --git a/dev/View/Popup/Filter.js b/dev/View/Popup/Filter.js index 98ca26c3e..bda7f1761 100644 --- a/dev/View/Popup/Filter.js +++ b/dev/View/Popup/Filter.js @@ -1,7 +1,7 @@ import ko from 'ko'; import { FiltersAction, FilterConditionField, FilterConditionType } from 'Common/Enums'; -import { defautOptionsAfterRender } from 'Common/Utils'; +import { defaultOptionsAfterRender } from 'Common/Utils'; import { i18n, initOnStartOrLangChange } from 'Common/Translator'; import FilterStore from 'Stores/User/Filter'; @@ -27,7 +27,7 @@ class FilterPopupView extends AbstractViewNext { this.allowMarkAsRead = ko.observable(false); - this.defautOptionsAfterRender = defautOptionsAfterRender; + this.defaultOptionsAfterRender = defaultOptionsAfterRender; this.folderSelectList = FolderStore.folderMenuForFilters; this.selectedFolderValue = ko.observable(''); diff --git a/dev/View/Popup/FolderCreate.js b/dev/View/Popup/FolderCreate.js index aaf2d7bcc..989f32a84 100644 --- a/dev/View/Popup/FolderCreate.js +++ b/dev/View/Popup/FolderCreate.js @@ -2,7 +2,8 @@ import ko from 'ko'; import { Notification } from 'Common/Enums'; import { UNUSED_OPTION_VALUE } from 'Common/Consts'; -import { defautOptionsAfterRender, folderListOptionsBuilder } from 'Common/Utils'; +import { defaultOptionsAfterRender } from 'Common/Utils'; +import { folderListOptionsBuilder } from 'Common/UtilsUser'; import FolderStore from 'Stores/User/Folder'; @@ -40,7 +41,7 @@ class FolderCreateView extends AbstractViewNext { return folderListOptionsBuilder([], list, [], top, null, fDisableCallback, null, fRenameCallback); }); - this.defautOptionsAfterRender = defautOptionsAfterRender; + this.defaultOptionsAfterRender = defaultOptionsAfterRender; } @command((self) => self.simpleFolderNameValidation(self.folderName())) diff --git a/dev/View/Popup/FolderSystem.js b/dev/View/Popup/FolderSystem.js index 55a5bcbce..2d723ed0e 100644 --- a/dev/View/Popup/FolderSystem.js +++ b/dev/View/Popup/FolderSystem.js @@ -2,7 +2,8 @@ import ko from 'ko'; import { SetSystemFoldersNotification } from 'Common/Enums'; import { UNUSED_OPTION_VALUE } from 'Common/Consts'; -import { folderListOptionsBuilder, defautOptionsAfterRender } from 'Common/Utils'; +import { defaultOptionsAfterRender } from 'Common/Utils'; +import { folderListOptionsBuilder } from 'Common/UtilsUser'; import { initOnStartOrLangChange, i18n } from 'Common/Translator'; import FolderStore from 'Stores/User/Folder'; @@ -84,7 +85,7 @@ class FolderSystemPopupView extends AbstractViewNext { FolderStore.trashFolder.subscribe(fCallback); FolderStore.archiveFolder.subscribe(fCallback); - this.defautOptionsAfterRender = defautOptionsAfterRender; + this.defaultOptionsAfterRender = defaultOptionsAfterRender; } /** diff --git a/dev/View/User/MailBox/MessageList.js b/dev/View/User/MailBox/MessageList.js index 76c71b5f6..22537ba12 100644 --- a/dev/View/User/MailBox/MessageList.js +++ b/dev/View/User/MailBox/MessageList.js @@ -15,7 +15,7 @@ import { UNUSED_OPTION_VALUE } from 'Common/Consts'; import { leftPanelDisabled, moveAction } from 'Common/Globals'; -import { computedPaginatorHelper } from 'Common/Utils'; +import { computedPaginatorHelper } from 'Common/UtilsUser'; import { File } from 'Common/File'; import { mailBox, append } from 'Common/Links'; diff --git a/dev/View/User/MailBox/MessageView.js b/dev/View/User/MailBox/MessageView.js index b399a6697..b3ef6ec36 100644 --- a/dev/View/User/MailBox/MessageView.js +++ b/dev/View/User/MailBox/MessageView.js @@ -15,11 +15,8 @@ import { import { $htmlCL, leftPanelDisabled, keyScopeReal, moveAction } from 'Common/Globals'; -import { - inFocus, - mailToHelper, - isTransparent -} from 'Common/Utils'; +import { inFocus } from 'Common/Utils'; +import { mailToHelper, isTransparent } from 'Common/UtilsUser'; import Audio from 'Common/Audio'; diff --git a/rainloop/v/0.0.0/app/templates/Views/Components/Select.html b/rainloop/v/0.0.0/app/templates/Views/Components/Select.html index 5d6532716..d664bbcff 100644 --- a/rainloop/v/0.0.0/app/templates/Views/Components/Select.html +++ b/rainloop/v/0.0.0/app/templates/Views/Components/Select.html @@ -4,7 +4,7 @@    + optionsCaption: optionsCaption, css: className, optionsAfterRender: defaultOptionsAfterRender">   @@ -17,4 +17,4 @@ params: { value: trigger } }"> - \ No newline at end of file + diff --git a/rainloop/v/0.0.0/app/templates/Views/User/PopupsFolderCreate.html b/rainloop/v/0.0.0/app/templates/Views/User/PopupsFolderCreate.html index f565322dd..f38ecea76 100644 --- a/rainloop/v/0.0.0/app/templates/Views/User/PopupsFolderCreate.html +++ b/rainloop/v/0.0.0/app/templates/Views/User/PopupsFolderCreate.html @@ -24,7 +24,7 @@
+ optionsText: 'name', optionsValue: 'id', optionsAfterRender: defaultOptionsAfterRender">
diff --git a/rainloop/v/0.0.0/app/templates/Views/User/PopupsFolderSystem.html b/rainloop/v/0.0.0/app/templates/Views/User/PopupsFolderSystem.html index 9262f69c4..d2236a878 100644 --- a/rainloop/v/0.0.0/app/templates/Views/User/PopupsFolderSystem.html +++ b/rainloop/v/0.0.0/app/templates/Views/User/PopupsFolderSystem.html @@ -17,7 +17,7 @@
+ optionsText: 'name', optionsValue: 'id', optionsAfterRender: $root.defaultOptionsAfterRender">
@@ -26,7 +26,7 @@
+ optionsText: 'name', optionsValue: 'id', optionsAfterRender: defaultOptionsAfterRender">
@@ -35,7 +35,7 @@
+ optionsText: 'name', optionsValue: 'id', optionsAfterRender: $root.defaultOptionsAfterRender">
@@ -44,7 +44,7 @@
+ optionsText: 'name', optionsValue: 'id', optionsAfterRender: $root.defaultOptionsAfterRender">
@@ -53,7 +53,7 @@
+ optionsText: 'name', optionsValue: 'id', optionsAfterRender: $root.defaultOptionsAfterRender">
diff --git a/rainloop/v/0.0.0/app/templates/Views/User/SettingsFiltersActionMoveToFolder.html b/rainloop/v/0.0.0/app/templates/Views/User/SettingsFiltersActionMoveToFolder.html index 44566d311..529d20fdf 100644 --- a/rainloop/v/0.0.0/app/templates/Views/User/SettingsFiltersActionMoveToFolder.html +++ b/rainloop/v/0.0.0/app/templates/Views/User/SettingsFiltersActionMoveToFolder.html @@ -1,7 +1,7 @@
+ optionsText: 'name', optionsValue: 'id', optionsAfterRender: $root.defaultOptionsAfterRender">