Compact style mutation fixes and improvements (#1268)
* Don't use the CSSOM when there's `var()` present as it fails badly https://github.com/rrweb-io/rrweb/pull/1246 * As the CSS Object Model expands out shorthand properties, do a check on the string length before choosing which format to go for - this approach allows 'var()' in a styleOMValue as it's only a problem when combined with a shorthand property - before this change background:black; was getting expaned to 10 OM properties as follows: 'style': { 'background-color': 'black', 'background-image': false, 'background-position-x': false, 'background-position-y': false, 'background-size': false, 'background-repeat-x': false, 'background-repeat-y': false, 'background-attachment': false, 'background-origin': false, 'background-clip': false } * Updates to remainder of tests based on refined compact style mutations * Apply suggestions from code review by: Justin Halsall <Juice10@users.noreply.github.com> --------- Authored-by: eoghanmurray <eoghan@getthere.ie>
This commit is contained in:
9
.changeset/clean-plants-play.md
Normal file
9
.changeset/clean-plants-play.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
'rrweb': patch
|
||||||
|
'@rrweb/types': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Compact style mutation fixes and improvements
|
||||||
|
|
||||||
|
- fixes when style updates contain a 'var()' on a shorthand property #1246
|
||||||
|
- further ensures that style mutations are compact by reverting to string method if it is shorter
|
||||||
@@ -18,7 +18,6 @@ import type {
|
|||||||
attributeCursor,
|
attributeCursor,
|
||||||
removedNodeMutation,
|
removedNodeMutation,
|
||||||
addedNodeMutation,
|
addedNodeMutation,
|
||||||
styleAttributeValue,
|
|
||||||
Optional,
|
Optional,
|
||||||
} from '@rrweb/types';
|
} from '@rrweb/types';
|
||||||
import {
|
import {
|
||||||
@@ -438,10 +437,29 @@ export default class MutationBuffer {
|
|||||||
// text mutation's id was not in the mirror map means the target node has been removed
|
// text mutation's id was not in the mirror map means the target node has been removed
|
||||||
.filter((text) => this.mirror.has(text.id)),
|
.filter((text) => this.mirror.has(text.id)),
|
||||||
attributes: this.attributes
|
attributes: this.attributes
|
||||||
.map((attribute) => ({
|
.map((attribute) => {
|
||||||
id: this.mirror.getId(attribute.node),
|
const { attributes } = attribute;
|
||||||
attributes: attribute.attributes,
|
if (typeof attributes.style === 'string') {
|
||||||
}))
|
const diffAsStr = JSON.stringify(attribute.styleDiff);
|
||||||
|
const unchangedAsStr = JSON.stringify(attribute._unchangedStyles);
|
||||||
|
// check if the style diff is actually shorter than the regular string based mutation
|
||||||
|
// (which was the whole point of #464 'compact style mutation').
|
||||||
|
if (diffAsStr.length < attributes.style.length) {
|
||||||
|
// also: CSSOM fails badly when var() is present on shorthand properties, so only proceed with
|
||||||
|
// the compact style mutation if these have all been accounted for
|
||||||
|
if (
|
||||||
|
(diffAsStr + unchangedAsStr).split('var(').length ===
|
||||||
|
attributes.style.split('var(').length
|
||||||
|
) {
|
||||||
|
attributes.style = attribute.styleDiff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
id: this.mirror.getId(attribute.node),
|
||||||
|
attributes: attributes,
|
||||||
|
};
|
||||||
|
})
|
||||||
// attribute mutation's id was not in the mirror map means the target node has been removed
|
// attribute mutation's id was not in the mirror map means the target node has been removed
|
||||||
.filter((attribute) => this.mirror.has(attribute.id)),
|
.filter((attribute) => this.mirror.has(attribute.id)),
|
||||||
removes: this.removes,
|
removes: this.removes,
|
||||||
@@ -548,6 +566,8 @@ export default class MutationBuffer {
|
|||||||
item = {
|
item = {
|
||||||
node: m.target,
|
node: m.target,
|
||||||
attributes: {},
|
attributes: {},
|
||||||
|
styleDiff: {},
|
||||||
|
_unchangedStyles: {},
|
||||||
};
|
};
|
||||||
this.attributes.push(item);
|
this.attributes.push(item);
|
||||||
}
|
}
|
||||||
@@ -562,39 +582,7 @@ export default class MutationBuffer {
|
|||||||
target.setAttribute('data-rr-is-password', 'true');
|
target.setAttribute('data-rr-is-password', 'true');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attributeName === 'style') {
|
if (!ignoreAttribute(target.tagName, attributeName, value)) {
|
||||||
const old = unattachedDoc.createElement('span');
|
|
||||||
if (m.oldValue) {
|
|
||||||
old.setAttribute('style', m.oldValue);
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
item.attributes.style === undefined ||
|
|
||||||
item.attributes.style === null
|
|
||||||
) {
|
|
||||||
item.attributes.style = {};
|
|
||||||
}
|
|
||||||
const styleObj = item.attributes.style as styleAttributeValue;
|
|
||||||
for (const pname of Array.from(target.style)) {
|
|
||||||
const newValue = target.style.getPropertyValue(pname);
|
|
||||||
const newPriority = target.style.getPropertyPriority(pname);
|
|
||||||
if (
|
|
||||||
newValue !== old.style.getPropertyValue(pname) ||
|
|
||||||
newPriority !== old.style.getPropertyPriority(pname)
|
|
||||||
) {
|
|
||||||
if (newPriority === '') {
|
|
||||||
styleObj[pname] = newValue;
|
|
||||||
} else {
|
|
||||||
styleObj[pname] = [newValue, newPriority];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const pname of Array.from(old.style)) {
|
|
||||||
if (target.style.getPropertyValue(pname) === '') {
|
|
||||||
// "if not set, returns the empty string"
|
|
||||||
styleObj[pname] = false; // delete
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (!ignoreAttribute(target.tagName, attributeName, value)) {
|
|
||||||
// overwrite attribute if the mutations was triggered in same time
|
// overwrite attribute if the mutations was triggered in same time
|
||||||
item.attributes[attributeName] = transformAttribute(
|
item.attributes[attributeName] = transformAttribute(
|
||||||
this.doc,
|
this.doc,
|
||||||
@@ -602,6 +590,35 @@ export default class MutationBuffer {
|
|||||||
toLowerCase(attributeName),
|
toLowerCase(attributeName),
|
||||||
value,
|
value,
|
||||||
);
|
);
|
||||||
|
if (attributeName === 'style') {
|
||||||
|
const old = unattachedDoc.createElement('span');
|
||||||
|
if (m.oldValue) {
|
||||||
|
old.setAttribute('style', m.oldValue);
|
||||||
|
}
|
||||||
|
for (const pname of Array.from(target.style)) {
|
||||||
|
const newValue = target.style.getPropertyValue(pname);
|
||||||
|
const newPriority = target.style.getPropertyPriority(pname);
|
||||||
|
if (
|
||||||
|
newValue !== old.style.getPropertyValue(pname) ||
|
||||||
|
newPriority !== old.style.getPropertyPriority(pname)
|
||||||
|
) {
|
||||||
|
if (newPriority === '') {
|
||||||
|
item.styleDiff[pname] = newValue;
|
||||||
|
} else {
|
||||||
|
item.styleDiff[pname] = [newValue, newPriority];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// for checking
|
||||||
|
item._unchangedStyles[pname] = [newValue, newPriority];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const pname of Array.from(old.style)) {
|
||||||
|
if (target.style.getPropertyValue(pname) === '') {
|
||||||
|
// "if not set, returns the empty string"
|
||||||
|
item.styleDiff[pname] = false; // delete
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2933,24 +2933,14 @@ exports[`record integration tests can record node mutations 1`] = `
|
|||||||
\\"id\\": 36,
|
\\"id\\": 36,
|
||||||
\\"attributes\\": {
|
\\"attributes\\": {
|
||||||
\\"id\\": \\"select2-drop\\",
|
\\"id\\": \\"select2-drop\\",
|
||||||
\\"style\\": {
|
\\"style\\": \\"left: Npx; width: Npx; top: Npx; bottom: auto; display: block;\\",
|
||||||
\\"left\\": \\"Npx\\",
|
|
||||||
\\"width\\": \\"Npx\\",
|
|
||||||
\\"top\\": \\"Npx\\",
|
|
||||||
\\"bottom\\": \\"auto\\",
|
|
||||||
\\"display\\": \\"block\\",
|
|
||||||
\\"position\\": false,
|
|
||||||
\\"visibility\\": false
|
|
||||||
},
|
|
||||||
\\"class\\": \\"select2-drop select2-display-none select2-with-searchbox select2-drop-active\\"
|
\\"class\\": \\"select2-drop select2-display-none select2-with-searchbox select2-drop-active\\"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
\\"id\\": 70,
|
\\"id\\": 70,
|
||||||
\\"attributes\\": {
|
\\"attributes\\": {
|
||||||
\\"style\\": {
|
\\"style\\": \\"\\"
|
||||||
\\"display\\": false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -3391,6 +3381,232 @@ exports[`record integration tests can record node mutations 1`] = `
|
|||||||
]"
|
]"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`record integration tests can record style changes compactly and preserve css var() functions 1`] = `
|
||||||
|
"[
|
||||||
|
{
|
||||||
|
\\"type\\": 0,
|
||||||
|
\\"data\\": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\\"type\\": 1,
|
||||||
|
\\"data\\": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\\"type\\": 4,
|
||||||
|
\\"data\\": {
|
||||||
|
\\"href\\": \\"about:blank\\",
|
||||||
|
\\"width\\": 1920,
|
||||||
|
\\"height\\": 1080
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\\"type\\": 2,
|
||||||
|
\\"data\\": {
|
||||||
|
\\"node\\": {
|
||||||
|
\\"type\\": 0,
|
||||||
|
\\"childNodes\\": [
|
||||||
|
{
|
||||||
|
\\"type\\": 2,
|
||||||
|
\\"tagName\\": \\"html\\",
|
||||||
|
\\"attributes\\": {},
|
||||||
|
\\"childNodes\\": [
|
||||||
|
{
|
||||||
|
\\"type\\": 2,
|
||||||
|
\\"tagName\\": \\"head\\",
|
||||||
|
\\"attributes\\": {},
|
||||||
|
\\"childNodes\\": [],
|
||||||
|
\\"id\\": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\\"type\\": 2,
|
||||||
|
\\"tagName\\": \\"body\\",
|
||||||
|
\\"attributes\\": {},
|
||||||
|
\\"childNodes\\": [
|
||||||
|
{
|
||||||
|
\\"type\\": 3,
|
||||||
|
\\"textContent\\": \\"\\\\n \\",
|
||||||
|
\\"id\\": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\\"type\\": 2,
|
||||||
|
\\"tagName\\": \\"script\\",
|
||||||
|
\\"attributes\\": {},
|
||||||
|
\\"childNodes\\": [
|
||||||
|
{
|
||||||
|
\\"type\\": 3,
|
||||||
|
\\"textContent\\": \\"SCRIPT_PLACEHOLDER\\",
|
||||||
|
\\"id\\": 7
|
||||||
|
}
|
||||||
|
],
|
||||||
|
\\"id\\": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\\"type\\": 3,
|
||||||
|
\\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\",
|
||||||
|
\\"id\\": 8
|
||||||
|
}
|
||||||
|
],
|
||||||
|
\\"id\\": 4
|
||||||
|
}
|
||||||
|
],
|
||||||
|
\\"id\\": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
\\"compatMode\\": \\"BackCompat\\",
|
||||||
|
\\"id\\": 1
|
||||||
|
},
|
||||||
|
\\"initialOffset\\": {
|
||||||
|
\\"left\\": 0,
|
||||||
|
\\"top\\": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\\"type\\": 3,
|
||||||
|
\\"data\\": {
|
||||||
|
\\"source\\": 0,
|
||||||
|
\\"texts\\": [],
|
||||||
|
\\"attributes\\": [
|
||||||
|
{
|
||||||
|
\\"id\\": 4,
|
||||||
|
\\"attributes\\": {
|
||||||
|
\\"style\\": \\"background: var(--mystery)\\"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
\\"removes\\": [],
|
||||||
|
\\"adds\\": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\\"type\\": 3,
|
||||||
|
\\"data\\": {
|
||||||
|
\\"source\\": 0,
|
||||||
|
\\"texts\\": [],
|
||||||
|
\\"attributes\\": [
|
||||||
|
{
|
||||||
|
\\"id\\": 4,
|
||||||
|
\\"attributes\\": {
|
||||||
|
\\"style\\": \\"background: var(--mystery); background-color: black\\"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
\\"removes\\": [],
|
||||||
|
\\"adds\\": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\\"type\\": 3,
|
||||||
|
\\"data\\": {
|
||||||
|
\\"source\\": 0,
|
||||||
|
\\"texts\\": [],
|
||||||
|
\\"attributes\\": [
|
||||||
|
{
|
||||||
|
\\"id\\": 4,
|
||||||
|
\\"attributes\\": {
|
||||||
|
\\"style\\": \\"\\"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
\\"removes\\": [],
|
||||||
|
\\"adds\\": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\\"type\\": 3,
|
||||||
|
\\"data\\": {
|
||||||
|
\\"source\\": 0,
|
||||||
|
\\"texts\\": [],
|
||||||
|
\\"attributes\\": [
|
||||||
|
{
|
||||||
|
\\"id\\": 4,
|
||||||
|
\\"attributes\\": {
|
||||||
|
\\"style\\": \\"display:block\\"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
\\"removes\\": [],
|
||||||
|
\\"adds\\": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\\"type\\": 3,
|
||||||
|
\\"data\\": {
|
||||||
|
\\"source\\": 0,
|
||||||
|
\\"texts\\": [],
|
||||||
|
\\"attributes\\": [
|
||||||
|
{
|
||||||
|
\\"id\\": 4,
|
||||||
|
\\"attributes\\": {
|
||||||
|
\\"style\\": {
|
||||||
|
\\"color\\": \\"var(--mystery-color)\\"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
\\"removes\\": [],
|
||||||
|
\\"adds\\": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\\"type\\": 3,
|
||||||
|
\\"data\\": {
|
||||||
|
\\"source\\": 0,
|
||||||
|
\\"texts\\": [],
|
||||||
|
\\"attributes\\": [
|
||||||
|
{
|
||||||
|
\\"id\\": 4,
|
||||||
|
\\"attributes\\": {
|
||||||
|
\\"style\\": \\"color:var(--mystery-color);display:block;margin:10px\\"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
\\"removes\\": [],
|
||||||
|
\\"adds\\": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\\"type\\": 3,
|
||||||
|
\\"data\\": {
|
||||||
|
\\"source\\": 0,
|
||||||
|
\\"texts\\": [],
|
||||||
|
\\"attributes\\": [
|
||||||
|
{
|
||||||
|
\\"id\\": 4,
|
||||||
|
\\"attributes\\": {
|
||||||
|
\\"style\\": {
|
||||||
|
\\"margin-left\\": \\"Npx\\"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
\\"removes\\": [],
|
||||||
|
\\"adds\\": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\\"type\\": 3,
|
||||||
|
\\"data\\": {
|
||||||
|
\\"source\\": 0,
|
||||||
|
\\"texts\\": [],
|
||||||
|
\\"attributes\\": [
|
||||||
|
{
|
||||||
|
\\"id\\": 4,
|
||||||
|
\\"attributes\\": {
|
||||||
|
\\"style\\": {
|
||||||
|
\\"margin-top\\": \\"Npx\\",
|
||||||
|
\\"color\\": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
\\"removes\\": [],
|
||||||
|
\\"adds\\": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`record integration tests can use maskInputOptions to configure which type of inputs should be masked 1`] = `
|
exports[`record integration tests can use maskInputOptions to configure which type of inputs should be masked 1`] = `
|
||||||
"[
|
"[
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -209,6 +209,56 @@ describe('record integration tests', function (this: ISuite) {
|
|||||||
assertSnapshot(snapshots);
|
assertSnapshot(snapshots);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('can record style changes compactly and preserve css var() functions', async () => {
|
||||||
|
const page: puppeteer.Page = await browser.newPage();
|
||||||
|
await page.goto('about:blank');
|
||||||
|
await page.setContent(getHtml.call(this, 'blank.html'), {
|
||||||
|
waitUntil: 'networkidle0',
|
||||||
|
});
|
||||||
|
|
||||||
|
// goal here is to ensure var(--mystery) ends up in the mutations (CSSOM fails in this case)
|
||||||
|
await page.evaluate(
|
||||||
|
'document.body.setAttribute("style", "background: var(--mystery)")',
|
||||||
|
);
|
||||||
|
await waitForRAF(page);
|
||||||
|
// and in this change we can't use the shorter styleObj format either
|
||||||
|
await page.evaluate(
|
||||||
|
'document.body.setAttribute("style", "background: var(--mystery); background-color: black")',
|
||||||
|
);
|
||||||
|
|
||||||
|
// reset is always shorter to be recorded as a sting rather than a styleObj
|
||||||
|
await page.evaluate('document.body.setAttribute("style", "")');
|
||||||
|
await waitForRAF(page);
|
||||||
|
|
||||||
|
await page.evaluate('document.body.setAttribute("style", "display:block")');
|
||||||
|
await waitForRAF(page);
|
||||||
|
// following should be recorded as an update of `{ color: 'var(--mystery-color)' }` without needing to include the display
|
||||||
|
await page.evaluate(
|
||||||
|
'document.body.setAttribute("style", "color:var(--mystery-color);display:block")',
|
||||||
|
);
|
||||||
|
await waitForRAF(page);
|
||||||
|
// whereas this case, it's shorter to record the entire string than the longhands for margin
|
||||||
|
await page.evaluate(
|
||||||
|
'document.body.setAttribute("style", "color:var(--mystery-color);display:block;margin:10px")',
|
||||||
|
);
|
||||||
|
await waitForRAF(page);
|
||||||
|
// and in this case, it's shorter to record just the change to the longhand margin-left;
|
||||||
|
await page.evaluate(
|
||||||
|
'document.body.setAttribute("style", "color:var(--mystery-color);display:block;margin:10px 10px 10px 0px;")',
|
||||||
|
);
|
||||||
|
await waitForRAF(page);
|
||||||
|
// see what happens when we manipulate the style object directly (expecting a compact mutation with just these two changes)
|
||||||
|
await page.evaluate(
|
||||||
|
'document.body.style.marginTop = 0; document.body.style.color = null',
|
||||||
|
);
|
||||||
|
await waitForRAF(page);
|
||||||
|
|
||||||
|
const snapshots = (await page.evaluate(
|
||||||
|
'window.snapshots',
|
||||||
|
)) as eventWithTime[];
|
||||||
|
assertSnapshot(snapshots);
|
||||||
|
});
|
||||||
|
|
||||||
it('can freeze mutations', async () => {
|
it('can freeze mutations', async () => {
|
||||||
const page: puppeteer.Page = await browser.newPage();
|
const page: puppeteer.Page = await browser.newPage();
|
||||||
await page.goto('about:blank');
|
await page.goto('about:blank');
|
||||||
|
|||||||
@@ -2625,10 +2625,7 @@ exports[`cross origin iframes form.html should map scroll events correctly 1`] =
|
|||||||
{
|
{
|
||||||
\\"id\\": 9,
|
\\"id\\": 9,
|
||||||
\\"attributes\\": {
|
\\"attributes\\": {
|
||||||
\\"style\\": {
|
\\"style\\": \\"width: Npx; height: Npx;\\"
|
||||||
\\"width\\": \\"Npx\\",
|
|
||||||
\\"height\\": \\"Npx\\"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -133,23 +133,26 @@ function stringifySnapshots(snapshots: eventWithTime[]): string {
|
|||||||
s.data.source === IncrementalSource.Mutation
|
s.data.source === IncrementalSource.Mutation
|
||||||
) {
|
) {
|
||||||
s.data.attributes.forEach((a) => {
|
s.data.attributes.forEach((a) => {
|
||||||
if (
|
if ('style' in a.attributes && a.attributes.style) {
|
||||||
'style' in a.attributes &&
|
if (typeof a.attributes.style === 'object') {
|
||||||
a.attributes.style &&
|
for (const [k, v] of Object.entries(a.attributes.style)) {
|
||||||
typeof a.attributes.style === 'object'
|
if (Array.isArray(v)) {
|
||||||
) {
|
if (coordinatesReg.test(k + ': ' + v[0])) {
|
||||||
for (const [k, v] of Object.entries(a.attributes.style)) {
|
// TODO: could round the number here instead depending on what's coming out of various test envs
|
||||||
if (Array.isArray(v)) {
|
a.attributes.style[k] = ['Npx', v[1]];
|
||||||
if (coordinatesReg.test(k + ': ' + v[0])) {
|
}
|
||||||
// TODO: could round the number here instead depending on what's coming out of various test envs
|
} else if (typeof v === 'string') {
|
||||||
a.attributes.style[k] = ['Npx', v[1]];
|
if (coordinatesReg.test(k + ': ' + v)) {
|
||||||
}
|
a.attributes.style[k] = 'Npx';
|
||||||
} else if (typeof v === 'string') {
|
}
|
||||||
if (coordinatesReg.test(k + ': ' + v)) {
|
|
||||||
a.attributes.style[k] = 'Npx';
|
|
||||||
}
|
}
|
||||||
|
coordinatesReg.lastIndex = 0; // wow, a real wart in ECMAScript
|
||||||
}
|
}
|
||||||
coordinatesReg.lastIndex = 0; // wow, a real wart in ECMAScript
|
} else if (coordinatesReg.test(a.attributes.style)) {
|
||||||
|
a.attributes.style = a.attributes.style.replace(
|
||||||
|
coordinatesReg,
|
||||||
|
'$1: Npx',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -283,7 +283,7 @@ export type textMutation = {
|
|||||||
value: string | null;
|
value: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type styleAttributeValue = {
|
export type styleOMValue = {
|
||||||
[key: string]: styleValueWithPriority | string | false;
|
[key: string]: styleValueWithPriority | string | false;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -292,13 +292,15 @@ export type styleValueWithPriority = [string, string];
|
|||||||
export type attributeCursor = {
|
export type attributeCursor = {
|
||||||
node: Node;
|
node: Node;
|
||||||
attributes: {
|
attributes: {
|
||||||
[key: string]: string | styleAttributeValue | null;
|
[key: string]: string | styleOMValue | null;
|
||||||
};
|
};
|
||||||
|
styleDiff: styleOMValue;
|
||||||
|
_unchangedStyles: styleOMValue;
|
||||||
};
|
};
|
||||||
export type attributeMutation = {
|
export type attributeMutation = {
|
||||||
id: number;
|
id: number;
|
||||||
attributes: {
|
attributes: {
|
||||||
[key: string]: string | styleAttributeValue | null;
|
[key: string]: string | styleOMValue | null;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user