mirror of
https://github.com/the-djmaze/snappymail.git
synced 2024-09-20 07:35:55 +08:00
Compare commits
7 commits
ed41d8e45f
...
cfbc47488a
Author | SHA1 | Date | |
---|---|---|---|
cfbc47488a | |||
05812c6be1 | |||
18452cc53c | |||
e0236ea52d | |||
6f4f6bfd03 | |||
f8520c27a2 | |||
61e7c00b62 |
|
@ -17,14 +17,34 @@ const
|
|||
"'": '''
|
||||
},
|
||||
|
||||
disallowedTags = [
|
||||
'svg','script','title','link','base','meta',
|
||||
'input','output','select','button','textarea',
|
||||
'bgsound','keygen','source','object','embed','applet','iframe','frame','frameset','video','audio','area','map'
|
||||
// not supported by <template> element
|
||||
// ,'html','head','body'
|
||||
keepTagContent = 'form,button,data', // font
|
||||
|
||||
allowedTags = [
|
||||
// Structural Elements:
|
||||
'blockquote','br','div','figcaption','figure','h1','h2','h3','h4','h5','h6','hgroup','hr','p','wbr',
|
||||
'article','aside','header','footer','main','section',
|
||||
'details','summary',
|
||||
// List Elements
|
||||
'dd','dl','dt','li','ol','ul',
|
||||
// Text Formatting Elements
|
||||
'a','abbr','address','b','bdi','bdo','cite','code','del','dfn',
|
||||
'em','i','ins','kbd','mark','pre','q','rp','rt','ruby','s','samp','small',
|
||||
'span','strong','sub','sup','time','u','var',
|
||||
// Deprecated by HTML Standard
|
||||
'acronym','big','center','dir','font','marquee',
|
||||
'nobr','noembed','noframes','plaintext','rb','rtc','strike','tt',
|
||||
// Media Elements
|
||||
'img',//'picture','source',
|
||||
// Table Elements
|
||||
'caption','col','colgroup','table','tbody','td','tfoot','th','thead','tr',
|
||||
// Disallowed but converted later
|
||||
'style','xmp'
|
||||
].join(','),
|
||||
|
||||
nonEmptyTags = [
|
||||
'A','B','EM','I','SPAN','STRONG'
|
||||
],
|
||||
|
||||
blockquoteSwitcher = () => {
|
||||
SettingsUserStore.collapseBlockquotes() &&
|
||||
// tmpl.content.querySelectorAll('blockquote').forEach(node => {
|
||||
|
@ -102,8 +122,10 @@ const
|
|||
},
|
||||
|
||||
cleanCSS = source =>
|
||||
source.trim().replace(/(^|;)\s*-(ms|webkit)-[^;]+(;|$)/g, '')
|
||||
.replace(/white-space[^;]+(;|$)/g, '')
|
||||
source.trim()
|
||||
.replace(/;\s*-[^;]+/g, '')
|
||||
.replace(/^\s*-[^;]+(;|$)/g, '')
|
||||
.replace(/white-space[^;]+/g, '')
|
||||
// Drop Microsoft Office style properties
|
||||
// .replace(/mso-[^:;]+:[^;]+/gi, '')
|
||||
,
|
||||
|
@ -145,14 +167,14 @@ const
|
|||
if (source) {
|
||||
source = source
|
||||
// strip comments
|
||||
.replace(/\/\*[\s\S]*?\*\/|<!--|-->/gi, '')
|
||||
// strip import statements
|
||||
.replace(/@import .*?;/gi , '')
|
||||
// strip keyframe statements
|
||||
.replace(/((@.*?keyframes [\s\S]*?){([\s\S]*?}\s*?)})/gi, '');
|
||||
.replace(/\/\*[\s\S]*?\*\//gi, '')
|
||||
// strip MS Word comments
|
||||
.replace(/<!--[\s\S]*?-->/gi, '');
|
||||
// strip HTML
|
||||
// .replace(/<\/?[a-z][\s\S]*?>/gi, '');
|
||||
|
||||
// unified regex to match css & media queries together
|
||||
let unified = /((\s*?(?:\/\*[\s\S]*?\*\/)?\s*?@media[\s\S]*?){([\s\S]*?)}\s*?})|(([\s\S]*?){([\s\S]*?)})/gi,
|
||||
let unified = /(?:(\s*?@(?:media)[\s\S]*?){([\s\S]*?)}\s*?})|(?:([\s\S]*?){([\s\S]*?)})/gi,
|
||||
arr;
|
||||
|
||||
while (true) {
|
||||
|
@ -161,7 +183,7 @@ const
|
|||
break;
|
||||
}
|
||||
|
||||
let selector = arr[arr[2] === undefined ? 5 : 2].split('\r\n').join('\n').trim()
|
||||
let selector = arr[arr[2] === undefined ? 3 : 1].split('\r\n').join('\n').trim()
|
||||
// Never have more than a single line break in a row
|
||||
.replace(/\n+/, "\n")
|
||||
// Remove :root and html
|
||||
|
@ -173,13 +195,14 @@ const
|
|||
css.push({
|
||||
selector: selector,
|
||||
type: 'media',
|
||||
subStyles: parseCSS(arr[3] + '\n}') //recursively parse media query inner css
|
||||
subStyles: parseCSS(arr[2] + '\n}') //recursively parse media query inner css
|
||||
});
|
||||
} else if (selector && !selector.includes('@')) {
|
||||
// we have standard css
|
||||
// ignores @import, @keyframe, @font-face statements
|
||||
css.push({
|
||||
selector: selector,
|
||||
rules: cleanCSS(arr[6])
|
||||
rules: cleanCSS(arr[4])
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -258,9 +281,6 @@ export const
|
|||
'abbr', 'scope',
|
||||
// td
|
||||
'colspan', 'rowspan', 'headers'
|
||||
],
|
||||
nonEmptyTags = [
|
||||
'A','B','EM','I','SPAN','STRONG'
|
||||
];
|
||||
|
||||
if (SettingsUserStore.allowStyles()) {
|
||||
|
@ -307,13 +327,21 @@ export const
|
|||
}
|
||||
});
|
||||
|
||||
// https://github.com/the-djmaze/snappymail/issues/1125
|
||||
tmpl.content.querySelectorAll(keepTagContent).forEach(oElement => replaceWithChildren(oElement));
|
||||
|
||||
tmpl.content.querySelectorAll(
|
||||
disallowedTags
|
||||
':not('+allowedTags+')'
|
||||
+ (0 < bqLevel ? ',' + (new Array(1 + bqLevel).fill('blockquote').join(' ')) : '')
|
||||
).forEach(oElement => oElement.remove());
|
||||
|
||||
// https://github.com/the-djmaze/snappymail/issues/1125
|
||||
tmpl.content.querySelectorAll('form,button').forEach(oElement => replaceWithChildren(oElement));
|
||||
/* // Is this slower or faster?
|
||||
).forEach(oElement => {
|
||||
if (!node || !node.contains(oElement)) {
|
||||
oElement.remove();
|
||||
node = oElement;
|
||||
}
|
||||
});
|
||||
*/
|
||||
|
||||
// https://github.com/the-djmaze/snappymail/issues/1641
|
||||
let body = tmpl.content.querySelector('.mail-body');
|
||||
|
@ -346,6 +374,13 @@ export const
|
|||
return;
|
||||
}
|
||||
|
||||
if ('XMP' === name) {
|
||||
const pre = createElement('pre');
|
||||
pre.innerHTML = encodeHtml(oElement.innerHTML);
|
||||
oElement.replaceWith(pre);
|
||||
return;
|
||||
}
|
||||
|
||||
// \MailSo\Base\HtmlUtils::ClearTags()
|
||||
if ('none' == oStyle.display
|
||||
|| 'hidden' == oStyle.visibility
|
||||
|
@ -432,7 +467,7 @@ export const
|
|||
}
|
||||
|
||||
// if (['CENTER','FORM'].includes(name)) {
|
||||
if ('O:P' === name || (nonEmptyTags.includes(name) && ('' == oElement.textContent.trim()))) {
|
||||
if (nonEmptyTags.includes(name) && ('' == oElement.textContent.trim())) {
|
||||
('A' !== name || !oElement.querySelector('IMG')) && replaceWithChildren(oElement);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -90,26 +90,23 @@ class SnappyMailHelper
|
|||
}
|
||||
*/
|
||||
if ($doLogin && $aCredentials[1] && $aCredentials[2]) {
|
||||
$isOIDC = \str_starts_with($aCredentials[2], 'oidc_login|');
|
||||
try {
|
||||
$ocSession = \OC::$server->getSession();
|
||||
if ($ocSession->get('is_oidc')) {
|
||||
$pwd = new \SnappyMail\SensitiveString($aCredentials[1]);
|
||||
$oAccount = $oActions->LoginProcess($aCredentials[1], $pwd);
|
||||
if ($oAccount) {
|
||||
$oActions->SetSignMeToken($oAccount);
|
||||
}
|
||||
} else {
|
||||
$oAccount = $oActions->LoginProcess($aCredentials[1], $aCredentials[2]);
|
||||
if ($oAccount && $oConfig->Get('login', 'sign_me_auto', \RainLoop\Enumerations\SignMeType::DefaultOff) === \RainLoop\Enumerations\SignMeType::DefaultOn) {
|
||||
$oActions->SetSignMeToken($oAccount);
|
||||
}
|
||||
$oAccount = $oActions->LoginProcess($aCredentials[1], $aCredentials[2]);
|
||||
if (!$isOIDC && $oAccount
|
||||
&& $oConfig->Get('login', 'sign_me_auto', \RainLoop\Enumerations\SignMeType::DefaultOff) === \RainLoop\Enumerations\SignMeType::DefaultOn
|
||||
) {
|
||||
$oActions->SetSignMeToken($oAccount);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
// Login failure, reset password to prevent more attempts
|
||||
$sUID = \OC::$server->getUserSession()->getUser()->getUID();
|
||||
\OC::$server->getSession()['snappymail-passphrase'] = '';
|
||||
\OC::$server->getConfig()->setUserValue($sUID, 'snappymail', 'passphrase', '');
|
||||
\SnappyMail\Log::error('Nextcloud', $e->getMessage());
|
||||
if (!$isOIDC) {
|
||||
$sUID = \OC::$server->getUserSession()->getUser()->getUID();
|
||||
\OC::$server->getSession()['snappymail-passphrase'] = '';
|
||||
\OC::$server->getConfig()->setUserValue($sUID, 'snappymail', 'passphrase', '');
|
||||
\SnappyMail\Log::error('Nextcloud', $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -126,6 +123,32 @@ class SnappyMailHelper
|
|||
}
|
||||
}
|
||||
|
||||
// Check if OpenID Connect (OIDC) is enabled and used for login
|
||||
// https://apps.nextcloud.com/apps/oidc_login
|
||||
public static function isOIDCLogin() : bool
|
||||
{
|
||||
$config = \OC::$server->getConfig();
|
||||
if ($config->getAppValue('snappymail', 'snappymail-autologin-oidc', false)) {
|
||||
// Check if the OIDC Login app is enabled
|
||||
if (\OC::$server->getAppManager()->isEnabledForUser('oidc_login')) {
|
||||
// Check if session is an OIDC Login
|
||||
$ocSession = \OC::$server->getSession();
|
||||
if ($ocSession->get('is_oidc')) {
|
||||
// IToken->getPassword() ???
|
||||
if ($ocSession->get('oidc_access_token')) {
|
||||
return true;
|
||||
}
|
||||
\SnappyMail\Log::debug('Nextcloud', 'OIDC access_token missing');
|
||||
} else {
|
||||
\SnappyMail\Log::debug('Nextcloud', 'No OIDC login');
|
||||
}
|
||||
} else {
|
||||
\SnappyMail\Log::debug('Nextcloud', 'OIDC login disabled');
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static function getLoginCredentials() : array
|
||||
{
|
||||
$sUID = \OC::$server->getUserSession()->getUser()->getUID();
|
||||
|
@ -151,18 +174,9 @@ class SnappyMailHelper
|
|||
if ($ocSession['snappymail-nc-uid'] == $sUID) {
|
||||
|
||||
// If OpenID Connect (OIDC) is enabled and used for login, use this.
|
||||
// https://apps.nextcloud.com/apps/oidc_login
|
||||
if ($config->getAppValue('snappymail', 'snappymail-autologin-oidc', false)) {
|
||||
if ($ocSession->get('is_oidc')) {
|
||||
// IToken->getPassword() ???
|
||||
if ($sAccessToken = $ocSession->get('oidc_access_token')) {
|
||||
$sEmail = $config->getUserValue($sUID, 'settings', 'email');
|
||||
return [$sUID, $sEmail, $sAccessToken];
|
||||
}
|
||||
\SnappyMail\Log::debug('Nextcloud', 'OIDC access_token missing');
|
||||
} else {
|
||||
\SnappyMail\Log::debug('Nextcloud', 'No OIDC login');
|
||||
}
|
||||
if (static::isOIDCLogin()) {
|
||||
$sEmail = $config->getUserValue($sUID, 'settings', 'email');
|
||||
return [$sUID, $sEmail, "oidc_login|{$sUID}"];
|
||||
}
|
||||
|
||||
// Only use the user's password in the current session if they have
|
||||
|
|
|
@ -4,8 +4,8 @@ class NextcloudPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
{
|
||||
const
|
||||
NAME = 'Nextcloud',
|
||||
VERSION = '2.37.0',
|
||||
RELEASE = '2024-08-11',
|
||||
VERSION = '2.37.1',
|
||||
RELEASE = '2024-09-16',
|
||||
CATEGORY = 'Integrations',
|
||||
DESCRIPTION = 'Integrate with Nextcloud v20+',
|
||||
REQUIRED = '2.36.2';
|
||||
|
@ -90,32 +90,17 @@ class NextcloudPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
|
||||
public function beforeLogin(\RainLoop\Model\Account $oAccount, \MailSo\Net\NetClient $oClient, \MailSo\Net\ConnectSettings $oSettings) : void
|
||||
{
|
||||
// https://apps.nextcloud.com/apps/oidc_login
|
||||
$config = \OC::$server->getConfig();
|
||||
$oUser = \OC::$server->getUserSession()->getUser();
|
||||
$sUID = $oUser->getUID();
|
||||
|
||||
$sEmail = $config->getUserValue($sUID, 'snappymail', 'snappymail-email');
|
||||
$sPassword = $config->getUserValue($sUID, 'snappymail', 'passphrase')
|
||||
?: $config->getUserValue($sUID, 'snappymail', 'snappymail-password');
|
||||
$bAccountDefinedExplicitly = ($sEmail && $sPassword) && $sEmail === $oSettings->username;
|
||||
|
||||
$sNcEmail = $oUser->getEMailAddress() ?: $oUser->getPrimaryEMailAddress();
|
||||
|
||||
// Only login with OIDC access token if
|
||||
// it is enabled in config, the user is currently logged in with OIDC,
|
||||
// the current snappymail account is the OIDC account and no account defined explicitly
|
||||
if (\OC::$server->getConfig()->getAppValue('snappymail', 'snappymail-autologin-oidc', false)
|
||||
&& \OC::$server->getSession()->get('is_oidc')
|
||||
&& $sNcEmail === $oSettings->username
|
||||
&& !$bAccountDefinedExplicitly
|
||||
if ($oAccount instanceof \RainLoop\Model\MainAccount
|
||||
&& \OCA\SnappyMail\Util\SnappyMailHelper::isOIDCLogin()
|
||||
// && $oClient->supportsAuthType('OAUTHBEARER') // v2.28
|
||||
&& \str_starts_with($oSettings->passphrase, 'oidc_login|')
|
||||
) {
|
||||
$sAccessToken = \OC::$server->getSession()->get('oidc_access_token');
|
||||
if ($sAccessToken) {
|
||||
$oSettings->passphrase = $sAccessToken;
|
||||
\array_unshift($oSettings->SASLMechanisms, 'OAUTHBEARER');
|
||||
}
|
||||
// $oSettings->passphrase = \OC::$server->getSession()->get('snappymail-passphrase');
|
||||
$oSettings->passphrase = \OC::$server->getSession()->get('oidc_access_token');
|
||||
\array_unshift($oSettings->SASLMechanisms, 'OAUTHBEARER');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -749,9 +749,14 @@ class MailClient
|
|||
*/
|
||||
public function MessageList(MessageListParams $oParams) : MessageCollection
|
||||
{
|
||||
if (0 > $oParams->iOffset || 0 > $oParams->iLimit || 999 < $oParams->iLimit) {
|
||||
if (0 > $oParams->iOffset || 0 > $oParams->iLimit) {
|
||||
throw new \ValueError;
|
||||
}
|
||||
if (10 > $oParams->iLimit) {
|
||||
$oParams->iLimit = 10;
|
||||
} else if (999 < $oParams->iLimit) {
|
||||
$oParams->iLimit = 50;
|
||||
}
|
||||
|
||||
$sSearch = \trim($oParams->sSearch);
|
||||
|
||||
|
|
6
vendors/bootstrap/less/forms.less
vendored
6
vendors/bootstrap/less/forms.less
vendored
|
@ -62,6 +62,12 @@ input {
|
|||
}
|
||||
}
|
||||
|
||||
//input:invalid,
|
||||
input:user-invalid {
|
||||
background-color: var(--error-bg-clr, #f2dede);
|
||||
border-color: var(--error-border-clr, #eed3d7);
|
||||
color: var(--error-clr, #b94a48);
|
||||
}
|
||||
|
||||
// Position radios and checkboxes better
|
||||
input[type="radio"],
|
||||
|
|
|
@ -51,6 +51,7 @@ ko.utils = {
|
|||
: node => node.cloneNode(true)),
|
||||
|
||||
setDomNodeChildren: (domNode, childNodes) => {
|
||||
// domNode.replaceChildren(...childNodes);
|
||||
ko.utils.emptyDomNode(domNode);
|
||||
childNodes && domNode.append(...childNodes);
|
||||
},
|
||||
|
@ -2821,7 +2822,7 @@ ko.bindingHandlers['textInput'] = {
|
|||
elementValueBeforeEvent = timeoutHandle = undefined;
|
||||
|
||||
var elementValue = element.value;
|
||||
if (previousElementValue !== elementValue) {
|
||||
if (element.checkValidity() && previousElementValue !== elementValue) {
|
||||
// Provide a way for tests to know exactly which event was processed
|
||||
previousElementValue = elementValue;
|
||||
ko.expressionRewriting.writeValueToProperty(valueAccessor(), allBindings, 'textInput', elementValue);
|
||||
|
|
|
@ -53,8 +53,8 @@ var ba={};c.i.options={init:a=>{if(!a.matches("SELECT"))throw Error("options bin
|
|||
e?f=b().map(c.C.M):0<=a.selectedIndex&&f.push(c.C.M(a.options[a.selectedIndex]));if(l){Array.isArray(l)||(l=[l]);var n=l.filter(m=>m??1)}var p=!1;l=k;d.has("optionsAfterRender")&&"function"==typeof d.get("optionsAfterRender")&&(l=(m,q)=>{k(m,q);c.u.I(d.get("optionsAfterRender"),null,[q[0],m!==ba?m:void 0])});c.g.Ab(a,n,(m,q,r)=>{r.length&&(f=r[0].selected?[c.C.M(r[0])]:[],p=!0);q=a.ownerDocument.createElement("option");m===ba?(c.g.Za(q),c.C.Fa(q,void 0)):(r=h(m,d.get("optionsValue"),m),c.C.Fa(q,c.g.h(r)),
|
||||
m=h(m,d.get("optionsText"),r),c.g.Za(q,m));return[q]},{},l);n=f.length;(e?n&&b().length<n:n&&0<=a.selectedIndex?c.C.M(a.options[a.selectedIndex])!==f[0]:n||0<=a.selectedIndex)&&c.u.I(c.g.Db,null,[a,"change"]);c.u.Ca()&&c.j.notify(a,c.j.F);g&&20<Math.abs(g-a.scrollTop)&&(a.scrollTop=g)}};c.i.options.Wa=c.g.l.Z();c.i.style={update:(a,b)=>{c.g.K(c.g.h(b()||{}),(d,e)=>{e=c.g.h(e);if(null==e||!1===e)e="";if(/^--/.test(d))a.style.setProperty(d,e);else{d=d.replace(/-(\w)/g,(l,f)=>f.toUpperCase());var g=
|
||||
a.style[d];a.style[d]=e;e===g||a.style[d]!=g||isNaN(e)||(a.style[d]=e+"px")}})}};c.i.submit={init:(a,b,d,e,g)=>{if("function"!=typeof b())throw Error("The value for a submit binding must be a function");a.addEventListener("submit",l=>{var f=b();try{var h=f.call(g.$data,a)}finally{!0!==h&&l.preventDefault()}})}};c.i.text={init:()=>({controlsDescendantBindings:!0}),update:(a,b)=>{8===a.nodeType&&(a.text||a.after(a.text=J.createTextNode("")),a=a.text);c.g.Za(a,b())}};c.m.aa.text=!0;c.i.textInput={init:(a,
|
||||
b,d)=>{var e=a.value,g,l,f=()=>{clearTimeout(g);l=g=void 0;var k=a.value;e!==k&&(e=k,c.la.Ga(b(),d,"textInput",k))},h=()=>{var k=c.g.h(b())??"";void 0!==l&&k===l?setTimeout(h,4):a.value!==k&&(a.value=k,e=a.value)};a.addEventListener("input",f);a.addEventListener("change",f);c.o(h,{s:a})}};c.i.value={init:(a,b,d)=>{var e=a.matches("SELECT"),g=a.matches("INPUT");if(!g||"checkbox"!=a.type&&"radio"!=a.type){var l=new Set,f=d.get("valueUpdate"),h=null,k=()=>{h=null;var m=b(),q=c.C.M(a);c.la.Ga(m,d,"value",
|
||||
q)};f&&("string"==typeof f?l.add(f):f.forEach(m=>l.add(m)),l.delete("change"));l.forEach(m=>{var q=k;(m||"").startsWith("after")&&(q=()=>{h=c.C.M(a);setTimeout(k,0)},m=m.slice(5));a.addEventListener(m,q)});var n=g&&"file"==a.type?()=>{var m=c.g.h(b());null==m||""===m?a.value="":c.u.I(k)}:()=>{var m=c.g.h(b()),q=c.C.M(a);if(null!==h&&m===h)setTimeout(n,0);else if(m!==q||void 0===q)e?(c.C.Fa(a,m),m!==c.C.M(a)&&c.u.I(k)):c.C.Fa(a,m)};if(e){var p;c.j.subscribe(a,c.j.F,()=>{p?d.get("valueAllowUnset")?
|
||||
b,d)=>{var e=a.value,g,l,f=()=>{clearTimeout(g);l=g=void 0;var k=a.value;a.checkValidity()&&e!==k&&(e=k,c.la.Ga(b(),d,"textInput",k))},h=()=>{var k=c.g.h(b())??"";void 0!==l&&k===l?setTimeout(h,4):a.value!==k&&(a.value=k,e=a.value)};a.addEventListener("input",f);a.addEventListener("change",f);c.o(h,{s:a})}};c.i.value={init:(a,b,d)=>{var e=a.matches("SELECT"),g=a.matches("INPUT");if(!g||"checkbox"!=a.type&&"radio"!=a.type){var l=new Set,f=d.get("valueUpdate"),h=null,k=()=>{h=null;var m=b(),q=c.C.M(a);
|
||||
c.la.Ga(m,d,"value",q)};f&&("string"==typeof f?l.add(f):f.forEach(m=>l.add(m)),l.delete("change"));l.forEach(m=>{var q=k;(m||"").startsWith("after")&&(q=()=>{h=c.C.M(a);setTimeout(k,0)},m=m.slice(5));a.addEventListener(m,q)});var n=g&&"file"==a.type?()=>{var m=c.g.h(b());null==m||""===m?a.value="":c.u.I(k)}:()=>{var m=c.g.h(b()),q=c.C.M(a);if(null!==h&&m===h)setTimeout(n,0);else if(m!==q||void 0===q)e?(c.C.Fa(a,m),m!==c.C.M(a)&&c.u.I(k)):c.C.Fa(a,m)};if(e){var p;c.j.subscribe(a,c.j.F,()=>{p?d.get("valueAllowUnset")?
|
||||
n():k():(a.addEventListener("change",k),p=c.o(n,{s:a}))},null,{notifyImmediately:!0})}else a.addEventListener("change",k),c.o(n,{s:a})}else c.applyBindingAccessorsToNode(a,{checkedValue:b})},update:()=>{}};c.i.visible={update:(a,b)=>{b=c.g.h(b());var d="none"!=a.style.display;b&&!d?a.style.display="":d&&!b&&(a.style.display="none")}};c.i.hidden={update:(a,b)=>a.hidden=!!c.g.h(b())};(function(a){c.i[a]={init:function(b,d,e,g,l){return c.i.event.init.call(this,b,()=>({[a]:d()}),e,g,l)}}})("click");
|
||||
(()=>{let a=c.g.l.Z();class b{constructor(e){this.Na=e}Ua(...e){let g=this.Na;if(!e.length)return c.g.l.get(g,a)||(11===this.H?g.content:1===this.H?g:void 0);c.g.l.set(g,a,e[0])}}class d extends b{constructor(e){super(e);e&&(this.H=e.matches("TEMPLATE")&&e.content?e.content.nodeType:1)}}c.bb={Na:d,lb:b}})();(()=>{const a=(h,k,n)=>{var p;for(k=c.m.nextSibling(k);h&&(p=h)!==k;)h=c.m.nextSibling(p),n(p,h)},b=(h,k)=>{if(h.length){var n=h[0],p=n.parentNode;a(n,h[h.length-1],m=>(1===m.nodeType||8===m.nodeType)&&
|
||||
c.Ib(k,m));c.g.xa(h,p)}},d=(h,k,n,p)=>{var m=(h&&(h.nodeType?h:0<h.length?h[0]:null)||n||{}).ownerDocument;if("string"==typeof n){m=m||J;m=m.getElementById(n);if(!m)throw Error("Cannot find template with ID "+n);n=new c.bb.Na(m)}else if([1,8].includes(n.nodeType))n=new c.bb.lb(n);else throw Error("Unknown template type: "+n);n=(n=n.Ua?n.Ua():null)?[...n.cloneNode(!0).childNodes]:null;if(!Array.isArray(n)||0<n.length&&"number"!=typeof n[0].nodeType)throw Error("Template engine must return an array of DOM nodes");
|
||||
|
|
|
@ -10,7 +10,7 @@ ko.bindingHandlers['textInput'] = {
|
|||
elementValueBeforeEvent = timeoutHandle = undefined;
|
||||
|
||||
var elementValue = element.value;
|
||||
if (previousElementValue !== elementValue) {
|
||||
if (element.checkValidity() && previousElementValue !== elementValue) {
|
||||
// Provide a way for tests to know exactly which event was processed
|
||||
previousElementValue = elementValue;
|
||||
ko.expressionRewriting.writeValueToProperty(valueAccessor(), allBindings, 'textInput', elementValue);
|
||||
|
|
305
vendors/squire/build/squire-raw.js
vendored
305
vendors/squire/build/squire-raw.js
vendored
|
@ -6,26 +6,39 @@
|
|||
var SHOW_ELEMENT_OR_TEXT = 5;
|
||||
|
||||
// source/node/TreeWalker.ts
|
||||
var FILTER_ACCEPT = NodeFilter.FILTER_ACCEPT;
|
||||
TreeWalker.prototype.previousPONode = function() {
|
||||
const root = this.root;
|
||||
let current = this.currentNode;
|
||||
let node = current.lastChild;
|
||||
while (!node && current) {
|
||||
if (current === this.root) {
|
||||
break;
|
||||
let node;
|
||||
while (true) {
|
||||
node = current.lastChild;
|
||||
while (!node && current) {
|
||||
if (current === root) {
|
||||
break;
|
||||
}
|
||||
node = current.previousSibling;
|
||||
if (!node) {
|
||||
current = current.parentNode;
|
||||
}
|
||||
}
|
||||
node = this.previousSibling();
|
||||
if (!node) {
|
||||
current = this.parentNode();
|
||||
return null;
|
||||
}
|
||||
const nodeType = node.nodeType;
|
||||
const nodeFilterType = nodeType === Node.ELEMENT_NODE ? NodeFilter.SHOW_ELEMENT : nodeType === Node.TEXT_NODE ? NodeFilter.SHOW_TEXT : 0;
|
||||
if (!!(nodeFilterType & this.whatToShow) && FILTER_ACCEPT === this.filter.acceptNode(node)) {
|
||||
this.currentNode = node;
|
||||
return node;
|
||||
}
|
||||
current = node;
|
||||
}
|
||||
node && (this.currentNode = node);
|
||||
return node;
|
||||
};
|
||||
var createTreeWalker = (root, whatToShow, filter) => document.createTreeWalker(
|
||||
root,
|
||||
whatToShow,
|
||||
{
|
||||
acceptNode: (node) => !filter || filter(node) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP
|
||||
acceptNode: (node) => !filter || filter(node) ? FILTER_ACCEPT : NodeFilter.FILTER_SKIP
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -46,7 +59,7 @@
|
|||
|
||||
// source/node/Category.ts
|
||||
var inlineNodeNames = /^(?:#text|A(?:BBR|CRONYM)?|B(?:R|D[IO])?|C(?:ITE|ODE)|D(?:ATA|EL|FN)|EM|FONT|HR|I(?:FRAME|MG|NPUT|NS)?|KBD|Q|R(?:P|T|UBY)|S(?:AMP|MALL|PAN|TR(?:IKE|ONG)|U[BP])?|TIME|U|VAR|WBR)$/;
|
||||
var leafNodeNames = /* @__PURE__ */ new Set(["BR", "HR", "IFRAME", "IMG", "INPUT"]);
|
||||
var leafNodeNames = /* @__PURE__ */ new Set(["BR", "HR", "IMG"]);
|
||||
var UNKNOWN = 0;
|
||||
var INLINE = 1;
|
||||
var BLOCK = 2;
|
||||
|
@ -55,9 +68,7 @@
|
|||
var resetNodeCategoryCache = () => {
|
||||
cache = /* @__PURE__ */ new WeakMap();
|
||||
};
|
||||
var isLeaf = (node) => {
|
||||
return leafNodeNames.has(node.nodeName);
|
||||
};
|
||||
var isLeaf = (node) => leafNodeNames.has(node.nodeName);
|
||||
var getNodeCategory = (node) => {
|
||||
switch (node.nodeType) {
|
||||
case TEXT_NODE:
|
||||
|
@ -82,15 +93,9 @@
|
|||
cache.set(node, nodeCategory);
|
||||
return nodeCategory;
|
||||
};
|
||||
var isInline = (node) => {
|
||||
return getNodeCategory(node) === INLINE;
|
||||
};
|
||||
var isBlock = (node) => {
|
||||
return getNodeCategory(node) === BLOCK;
|
||||
};
|
||||
var isContainer = (node) => {
|
||||
return getNodeCategory(node) === CONTAINER;
|
||||
};
|
||||
var isInline = (node) => getNodeCategory(node) === INLINE;
|
||||
var isBlock = (node) => getNodeCategory(node) === BLOCK;
|
||||
var isContainer = (node) => getNodeCategory(node) === CONTAINER;
|
||||
|
||||
// source/node/Node.ts
|
||||
var createElement = (tag, props, children) => {
|
||||
|
@ -161,31 +166,17 @@
|
|||
}
|
||||
return returnNode;
|
||||
};
|
||||
var getLength = (node) => {
|
||||
return node instanceof Element || node instanceof DocumentFragment ? node.childNodes.length : node instanceof CharacterData ? node.length : 0;
|
||||
};
|
||||
var getLength = (node) => node instanceof Element || node instanceof DocumentFragment ? node.childNodes.length : node instanceof CharacterData ? node.length : 0;
|
||||
var empty = (node) => {
|
||||
const frag = document.createDocumentFragment();
|
||||
let child = node.firstChild;
|
||||
while (child) {
|
||||
frag.append(child);
|
||||
child = node.firstChild;
|
||||
}
|
||||
frag.append(...node.childNodes);
|
||||
return frag;
|
||||
};
|
||||
var detach = (node) => {
|
||||
const parent = node.parentNode;
|
||||
if (parent) {
|
||||
parent.removeChild(node);
|
||||
}
|
||||
node.parentNode?.removeChild(node);
|
||||
return node;
|
||||
};
|
||||
var replaceWith = (node, node2) => {
|
||||
const parent = node.parentNode;
|
||||
if (parent) {
|
||||
parent.replaceChild(node2, node);
|
||||
}
|
||||
};
|
||||
var replaceWith = (node, node2) => node.parentNode?.replaceChild(node2, node);
|
||||
var getClosest = (node, root, selector) => {
|
||||
node = (node && !node.closest ? node.parentElement : node)?.closest(selector);
|
||||
return node && root.contains(node) ? node : null;
|
||||
|
@ -203,12 +194,10 @@
|
|||
};
|
||||
|
||||
// source/node/Whitespace.ts
|
||||
var notWSTextNode = (node) => {
|
||||
return node instanceof Element ? node.nodeName === "BR" : (
|
||||
// okay if data is 'undefined' here.
|
||||
notWS.test(node.data)
|
||||
);
|
||||
};
|
||||
var notWSTextNode = (node) => node instanceof Element ? node.nodeName === "BR" : (
|
||||
// okay if data is 'undefined' here.
|
||||
notWS.test(node.data)
|
||||
);
|
||||
var isLineBreak = (br, isLBIfEmptyBlock) => {
|
||||
let block = br.parentNode;
|
||||
while (isInline(block)) {
|
||||
|
@ -291,7 +280,7 @@
|
|||
const child = endContainer.childNodes[endOffset - 1];
|
||||
if (!child || isLeaf(child)) {
|
||||
if (child && child.nodeName === "BR" && !isLineBreak(child, false)) {
|
||||
endOffset -= 1;
|
||||
--endOffset;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
@ -335,7 +324,7 @@
|
|||
break;
|
||||
}
|
||||
if (endContainer.nodeType !== TEXT_NODE && endContainer.childNodes[endOffset] && endContainer.childNodes[endOffset].nodeName === "BR" && !isLineBreak(endContainer.childNodes[endOffset], false)) {
|
||||
endOffset += 1;
|
||||
++endOffset;
|
||||
}
|
||||
if (endOffset !== getLength(endContainer)) {
|
||||
break;
|
||||
|
@ -400,32 +389,24 @@
|
|||
};
|
||||
var fixContainer = (container, root) => {
|
||||
let wrapper = null;
|
||||
Array.from(container.childNodes).forEach((child) => {
|
||||
[...container.childNodes].forEach((child) => {
|
||||
const isBR = child.nodeName === "BR";
|
||||
if (!isBR && isInline(child)) {
|
||||
if (!wrapper) {
|
||||
wrapper = createElement("DIV");
|
||||
}
|
||||
if (!isBR && child.parentNode == root && isInline(child)) {
|
||||
wrapper || (wrapper = createElement("DIV"));
|
||||
wrapper.append(child);
|
||||
} else if (isBR || wrapper) {
|
||||
if (!wrapper) {
|
||||
wrapper = createElement("DIV");
|
||||
}
|
||||
wrapper || (wrapper = createElement("DIV"));
|
||||
fixCursor(wrapper);
|
||||
if (isBR) {
|
||||
container.replaceChild(wrapper, child);
|
||||
child.replaceWith(wrapper);
|
||||
} else {
|
||||
container.insertBefore(wrapper, child);
|
||||
}
|
||||
wrapper = null;
|
||||
}
|
||||
if (isContainer(child)) {
|
||||
fixContainer(child, root);
|
||||
}
|
||||
isContainer(child) && fixContainer(child, root);
|
||||
});
|
||||
if (wrapper) {
|
||||
container.append(fixCursor(wrapper));
|
||||
}
|
||||
wrapper && container.append(fixCursor(wrapper));
|
||||
return container;
|
||||
};
|
||||
var split = (node, offset, stopNode, root) => {
|
||||
|
@ -454,7 +435,7 @@
|
|||
}
|
||||
fixCursor(node);
|
||||
fixCursor(clone);
|
||||
parent.insertBefore(clone, node.nextSibling);
|
||||
node.after(clone);
|
||||
return split(parent, clone, stopNode, root);
|
||||
};
|
||||
var _mergeInlines = (node, fakeRange) => {
|
||||
|
@ -475,7 +456,7 @@
|
|||
}
|
||||
if (fakeRange.startContainer === node) {
|
||||
if (fakeRange.startOffset > l) {
|
||||
fakeRange.startOffset -= 1;
|
||||
--fakeRange.startOffset;
|
||||
} else if (fakeRange.startOffset === l) {
|
||||
fakeRange.startContainer = prev;
|
||||
fakeRange.startOffset = getLength(prev);
|
||||
|
@ -483,7 +464,7 @@
|
|||
}
|
||||
if (fakeRange.endContainer === node) {
|
||||
if (fakeRange.endOffset > l) {
|
||||
fakeRange.endOffset -= 1;
|
||||
--fakeRange.endOffset;
|
||||
} else if (fakeRange.endOffset === l) {
|
||||
fakeRange.endContainer = prev;
|
||||
fakeRange.endOffset = getLength(prev);
|
||||
|
@ -529,8 +510,8 @@
|
|||
offset = block.childNodes.length;
|
||||
const last = block.lastChild;
|
||||
if (last && last.nodeName === "BR") {
|
||||
block.removeChild(last);
|
||||
offset -= 1;
|
||||
last.remove();
|
||||
--offset;
|
||||
}
|
||||
block.append(empty(next));
|
||||
range.setStart(block, offset);
|
||||
|
@ -546,23 +527,18 @@
|
|||
}
|
||||
if (prev && areAlike(prev, node)) {
|
||||
if (!isContainer(prev)) {
|
||||
if (isListItem) {
|
||||
const block = createElement("DIV");
|
||||
block.append(empty(prev));
|
||||
prev.append(block);
|
||||
} else {
|
||||
if (!isListItem) {
|
||||
return;
|
||||
}
|
||||
const block = createElement("DIV");
|
||||
block.append(empty(prev));
|
||||
prev.append(block);
|
||||
}
|
||||
detach(node);
|
||||
const needsFix = !isContainer(node);
|
||||
prev.append(empty(node));
|
||||
if (needsFix) {
|
||||
fixContainer(prev, root);
|
||||
}
|
||||
if (first) {
|
||||
mergeContainers(first, root);
|
||||
}
|
||||
needsFix && fixContainer(prev, root);
|
||||
first && mergeContainers(first, root);
|
||||
} else if (isListItem) {
|
||||
const block = createElement("DIV");
|
||||
node.insertBefore(block, first);
|
||||
|
@ -645,7 +621,7 @@
|
|||
return (node, parent) => {
|
||||
const el = createElement(tag);
|
||||
const attributes = node.attributes;
|
||||
for (let i = 0, l = attributes.length; i < l; i += 1) {
|
||||
for (let i = 0, l = attributes.length; i < l; ++i) {
|
||||
const attribute = attributes[i];
|
||||
el.setAttribute(attribute.name, attribute.value);
|
||||
}
|
||||
|
@ -655,13 +631,15 @@
|
|||
};
|
||||
};
|
||||
var fontSizes = {
|
||||
"1": "10",
|
||||
"2": "13",
|
||||
"3": "16",
|
||||
"4": "18",
|
||||
"5": "24",
|
||||
"6": "32",
|
||||
"7": "48"
|
||||
"1": "x-small",
|
||||
"2": "small",
|
||||
"3": "medium",
|
||||
"4": "large",
|
||||
"5": "x-large",
|
||||
"6": "xx-large",
|
||||
"7": "xxx-large",
|
||||
"-1": "smaller",
|
||||
"+1": "larger"
|
||||
};
|
||||
var stylesRewriters = {
|
||||
STRONG: replaceWithTag("B"),
|
||||
|
@ -674,62 +652,31 @@
|
|||
const face = font.face;
|
||||
const size = font.size;
|
||||
let color = font.color;
|
||||
const classNames = config.classNames;
|
||||
let fontSpan;
|
||||
let sizeSpan;
|
||||
let colorSpan;
|
||||
let newTreeBottom;
|
||||
let newTreeTop;
|
||||
let newTag = createElement("SPAN");
|
||||
let css = newTag.style;
|
||||
newTag.style.cssText = node.style.cssText;
|
||||
if (face) {
|
||||
fontSpan = createElement("SPAN", {
|
||||
class: classNames.fontFamily,
|
||||
style: "font-family:" + face
|
||||
});
|
||||
newTreeTop = fontSpan;
|
||||
newTreeBottom = fontSpan;
|
||||
css.fontFamily = face;
|
||||
}
|
||||
if (size) {
|
||||
sizeSpan = createElement("SPAN", {
|
||||
class: classNames.fontSize,
|
||||
style: "font-size:" + fontSizes[size] + "px"
|
||||
});
|
||||
if (!newTreeTop) {
|
||||
newTreeTop = sizeSpan;
|
||||
}
|
||||
if (newTreeBottom) {
|
||||
newTreeBottom.append(sizeSpan);
|
||||
}
|
||||
newTreeBottom = sizeSpan;
|
||||
css.fontSize = fontSizes[size];
|
||||
}
|
||||
if (color && /^#?([\dA-F]{3}){1,2}$/i.test(color)) {
|
||||
if (color.charAt(0) !== "#") {
|
||||
color = "#" + color;
|
||||
}
|
||||
colorSpan = createElement("SPAN", {
|
||||
class: classNames.color,
|
||||
style: "color:" + color
|
||||
});
|
||||
if (!newTreeTop) {
|
||||
newTreeTop = colorSpan;
|
||||
}
|
||||
if (newTreeBottom) {
|
||||
newTreeBottom.append(colorSpan);
|
||||
}
|
||||
newTreeBottom = colorSpan;
|
||||
css.color = color;
|
||||
}
|
||||
if (!newTreeTop || !newTreeBottom) {
|
||||
newTreeTop = newTreeBottom = createElement("SPAN");
|
||||
}
|
||||
parent.replaceChild(newTreeTop, font);
|
||||
newTreeBottom.append(empty(font));
|
||||
return newTreeBottom;
|
||||
replaceWith(node, newTag);
|
||||
newTag.append(empty(node));
|
||||
return newTag;
|
||||
},
|
||||
TT: (node, parent, config) => {
|
||||
const el = createElement("SPAN", {
|
||||
class: config.classNames.fontFamily,
|
||||
style: 'font-family:menlo,consolas,"courier new",monospace'
|
||||
});
|
||||
parent.replaceChild(el, node);
|
||||
replaceWith(node, el);
|
||||
el.append(empty(node));
|
||||
return el;
|
||||
}
|
||||
|
@ -788,7 +735,7 @@
|
|||
const brs = node.querySelectorAll("BR");
|
||||
const brBreaksLine = [];
|
||||
let l = brs.length;
|
||||
for (let i = 0; i < l; i += 1) {
|
||||
for (let i = 0; i < l; ++i) {
|
||||
brBreaksLine[i] = isLineBreak(brs[i], keepForBlankLine);
|
||||
}
|
||||
while (l--) {
|
||||
|
@ -869,7 +816,7 @@
|
|||
let nodeAfterCursor;
|
||||
if (startContainer instanceof Text) {
|
||||
const text = startContainer.data;
|
||||
for (let i = startOffset; i > 0; i -= 1) {
|
||||
for (let i = startOffset; i > 0; --i) {
|
||||
if (text.charAt(i - 1) !== ZWS) {
|
||||
return false;
|
||||
}
|
||||
|
@ -906,7 +853,7 @@
|
|||
if (endContainer instanceof Text) {
|
||||
const text = endContainer.data;
|
||||
const length = text.length;
|
||||
for (let i = endOffset; i < length; i += 1) {
|
||||
for (let i = endOffset; i < length; ++i) {
|
||||
if (text.charAt(i) !== ZWS) {
|
||||
return false;
|
||||
}
|
||||
|
@ -969,7 +916,7 @@
|
|||
endOffset -= startOffset;
|
||||
endContainer = afterSplit;
|
||||
} else if (endContainer === parent) {
|
||||
endOffset += 1;
|
||||
++endOffset;
|
||||
}
|
||||
startContainer = afterSplit;
|
||||
}
|
||||
|
@ -1223,7 +1170,7 @@
|
|||
let textContent = "";
|
||||
let addedTextInBlock = false;
|
||||
let value;
|
||||
if (!(node instanceof Element) && !(node instanceof Text) || NodeFilter.FILTER_ACCEPT !== walker.filter.acceptNode(node)) {
|
||||
if (!(node instanceof Element) && !(node instanceof Text) || FILTER_ACCEPT !== walker.filter.acceptNode(node)) {
|
||||
node = walker.nextNode();
|
||||
}
|
||||
while (node) {
|
||||
|
@ -2271,7 +2218,7 @@
|
|||
);
|
||||
let endOffset = Array.from(endContainer.childNodes).indexOf(end);
|
||||
if (startContainer === endContainer) {
|
||||
endOffset -= 1;
|
||||
--endOffset;
|
||||
}
|
||||
start.remove();
|
||||
end.remove();
|
||||
|
@ -2509,7 +2456,7 @@
|
|||
}
|
||||
const html = this._getRawHTML();
|
||||
if (replace) {
|
||||
undoIndex -= 1;
|
||||
--undoIndex;
|
||||
}
|
||||
if (undoThreshold > -1 && html.length * 2 > undoThreshold) {
|
||||
if (undoLimit > -1 && undoIndex > undoLimit) {
|
||||
|
@ -2520,15 +2467,13 @@
|
|||
}
|
||||
undoStack[undoIndex] = html;
|
||||
this._undoIndex = undoIndex;
|
||||
this._undoStackLength += 1;
|
||||
++this._undoStackLength;
|
||||
this._isInUndoState = true;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
saveUndoState(range) {
|
||||
if (!range) {
|
||||
range = this.getSelection();
|
||||
}
|
||||
range || (range = this.getSelection());
|
||||
this._recordUndoState(range, this._isInUndoState);
|
||||
this._getRangeAndRemoveBookmark(range);
|
||||
return this;
|
||||
|
@ -2536,7 +2481,7 @@
|
|||
undo() {
|
||||
if (this._undoIndex !== 0 || !this._isInUndoState) {
|
||||
this._recordUndoState(this.getSelection(), false);
|
||||
this._undoIndex -= 1;
|
||||
--this._undoIndex;
|
||||
this._setRawHTML(this._undoStack[this._undoIndex]);
|
||||
const range = this._getRangeAndRemoveBookmark();
|
||||
if (range) {
|
||||
|
@ -2555,7 +2500,7 @@
|
|||
const undoIndex = this._undoIndex;
|
||||
const undoStackLength = this._undoStackLength;
|
||||
if (undoIndex + 1 < undoStackLength && this._isInUndoState) {
|
||||
this._undoIndex += 1;
|
||||
++this._undoIndex;
|
||||
this._setRawHTML(this._undoStack[this._undoIndex]);
|
||||
const range = this._getRangeAndRemoveBookmark();
|
||||
if (range) {
|
||||
|
@ -2577,23 +2522,15 @@
|
|||
return this._root.innerHTML;
|
||||
}
|
||||
_setRawHTML(html) {
|
||||
const root = this._root;
|
||||
root.innerHTML = html;
|
||||
let node = root;
|
||||
const child = node.firstChild;
|
||||
if (!child || child.nodeName === "BR") {
|
||||
const block = this.createDefaultBlock();
|
||||
if (child) {
|
||||
node.replaceChild(block, child);
|
||||
} else {
|
||||
node.append(block);
|
||||
}
|
||||
} else {
|
||||
while (node = getNextBlock(node, root)) {
|
||||
if (html !== void 0) {
|
||||
const root = this._root;
|
||||
let node = root;
|
||||
root.innerHTML = html;
|
||||
do {
|
||||
fixCursor(node);
|
||||
}
|
||||
} while (node = getNextBlock(node, root));
|
||||
this._ignoreChange = true;
|
||||
}
|
||||
this._ignoreChange = true;
|
||||
return this;
|
||||
}
|
||||
getHTML(withBookmark) {
|
||||
|
@ -2798,7 +2735,7 @@
|
|||
openBlock += " " + attr + '="' + escapeHTML(attributes[attr]) + '"';
|
||||
}
|
||||
openBlock += ">";
|
||||
for (let i = 0, l = lines.length; i < l; i += 1) {
|
||||
for (let i = 0, l = lines.length; i < l; ++i) {
|
||||
let line = lines[i];
|
||||
line = escapeHTML(line).replace(/ (?=(?: |$))/g, " ");
|
||||
if (i) {
|
||||
|
@ -2839,22 +2776,22 @@
|
|||
const color = style.color;
|
||||
if (!fontInfo.color && color) {
|
||||
fontInfo.color = color;
|
||||
seenAttributes += 1;
|
||||
++seenAttributes;
|
||||
}
|
||||
const backgroundColor = style.backgroundColor;
|
||||
if (!fontInfo.backgroundColor && backgroundColor) {
|
||||
fontInfo.backgroundColor = backgroundColor;
|
||||
seenAttributes += 1;
|
||||
++seenAttributes;
|
||||
}
|
||||
const fontFamily = style.fontFamily;
|
||||
if (!fontInfo.fontFamily && fontFamily) {
|
||||
fontInfo.fontFamily = fontFamily;
|
||||
seenAttributes += 1;
|
||||
++seenAttributes;
|
||||
}
|
||||
const fontSize = style.fontSize;
|
||||
if (!fontInfo.fontSize && fontSize) {
|
||||
fontInfo.fontSize = fontSize;
|
||||
seenAttributes += 1;
|
||||
++seenAttributes;
|
||||
}
|
||||
}
|
||||
element = element.parentNode;
|
||||
|
@ -2868,12 +2805,8 @@
|
|||
*/
|
||||
hasFormat(tag, attributes, range) {
|
||||
tag = tag.toUpperCase();
|
||||
if (!attributes) {
|
||||
attributes = {};
|
||||
}
|
||||
if (!range) {
|
||||
range = this.getSelection();
|
||||
}
|
||||
attributes || (attributes = {});
|
||||
range || (range = this.getSelection());
|
||||
if (!range.collapsed && range.startContainer instanceof Text && range.startOffset === range.startContainer.length && range.startContainer.nextSibling) {
|
||||
range.setStartBefore(range.startContainer.nextSibling);
|
||||
}
|
||||
|
@ -2888,9 +2821,11 @@
|
|||
if (common instanceof Text) {
|
||||
return false;
|
||||
}
|
||||
const walker = createTreeWalker(common, SHOW_TEXT, (node2) => {
|
||||
return isNodeContainedInRange(range, node2, true);
|
||||
});
|
||||
const walker = createTreeWalker(
|
||||
common,
|
||||
SHOW_TEXT,
|
||||
(node2) => isNodeContainedInRange(range, node2, true)
|
||||
);
|
||||
let seenNode = false;
|
||||
let node;
|
||||
while (node = walker.nextNode()) {
|
||||
|
@ -2949,7 +2884,7 @@
|
|||
);
|
||||
let { startContainer, startOffset, endContainer, endOffset } = range;
|
||||
walker.currentNode = startContainer;
|
||||
if (!(startContainer instanceof Element) && !(startContainer instanceof Text) || NodeFilter.FILTER_ACCEPT !== walker.filter.acceptNode(startContainer)) {
|
||||
if (!(startContainer instanceof Element) && !(startContainer instanceof Text) || FILTER_ACCEPT !== walker.filter.acceptNode(startContainer)) {
|
||||
const next = walker.nextNode();
|
||||
if (!next) {
|
||||
return range;
|
||||
|
@ -2970,7 +2905,7 @@
|
|||
endContainer = node;
|
||||
endOffset -= startOffset;
|
||||
} else if (endContainer === startContainer.parentNode) {
|
||||
endOffset += 1;
|
||||
++endOffset;
|
||||
}
|
||||
startContainer = node;
|
||||
startOffset = 0;
|
||||
|
@ -3041,33 +2976,23 @@
|
|||
).filter((el) => {
|
||||
return isNodeContainedInRange(range, el, true) && hasTagAttributes(el, tag, attributes);
|
||||
});
|
||||
if (!partial) {
|
||||
formatTags.forEach((node) => {
|
||||
examineNode(node, node);
|
||||
});
|
||||
}
|
||||
partial || formatTags.forEach((node) => examineNode(node, node));
|
||||
toWrap.forEach(([el, node]) => {
|
||||
el = el.cloneNode(false);
|
||||
replaceWith(node, el);
|
||||
el.append(node);
|
||||
});
|
||||
formatTags.forEach((el) => {
|
||||
replaceWith(el, empty(el));
|
||||
});
|
||||
formatTags.forEach((el) => replaceWith(el, empty(el)));
|
||||
if (cantFocusEmptyTextNodes && fixer) {
|
||||
fixer = fixer.parentNode;
|
||||
let block = fixer;
|
||||
while (block && isInline(block)) {
|
||||
block = block.parentNode;
|
||||
}
|
||||
if (block) {
|
||||
removeZWS(block, fixer);
|
||||
}
|
||||
block && removeZWS(block, fixer);
|
||||
}
|
||||
this._getRangeAndRemoveBookmark(range);
|
||||
if (fixer) {
|
||||
range.collapse(false);
|
||||
}
|
||||
fixer && range.collapse(false);
|
||||
mergeInlines(root, range);
|
||||
return range;
|
||||
}
|
||||
|
@ -3097,7 +3022,7 @@
|
|||
let protocolEnd = url.indexOf(":") + 1;
|
||||
if (protocolEnd) {
|
||||
while (url[protocolEnd] === "/") {
|
||||
protocolEnd += 1;
|
||||
++protocolEnd;
|
||||
}
|
||||
}
|
||||
insertNodeInRange(
|
||||
|
@ -3574,13 +3499,13 @@
|
|||
const lists = frag.querySelectorAll("UL, OL");
|
||||
const items = frag.querySelectorAll("LI");
|
||||
const root = this._root;
|
||||
for (let i = 0, l = lists.length; i < l; i += 1) {
|
||||
for (let i = 0, l = lists.length; i < l; ++i) {
|
||||
const list = lists[i];
|
||||
const listFrag = empty(list);
|
||||
fixContainer(listFrag, root);
|
||||
replaceWith(list, listFrag);
|
||||
}
|
||||
for (let i = 0, l = items.length; i < l; i += 1) {
|
||||
for (let i = 0, l = items.length; i < l; ++i) {
|
||||
const item = items[i];
|
||||
if (isBlock(item)) {
|
||||
replaceWith(item, this.createDefaultBlock([empty(item)]));
|
||||
|
@ -3656,7 +3581,7 @@
|
|||
let nodes = node.querySelectorAll("BR");
|
||||
const brBreaksLine = [];
|
||||
let l = nodes.length;
|
||||
for (let i = 0; i < l; i += 1) {
|
||||
for (let i = 0; i < l; ++i) {
|
||||
brBreaksLine[i] = isLineBreak(nodes[i], false);
|
||||
}
|
||||
while (l--) {
|
||||
|
|
Loading…
Reference in a new issue