Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

T1113606: Fix table attributors keyNames collision. #70

Merged
merged 5 commits into from
Dec 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions attributors/attributor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Attributor } from 'parchment';
import { decorateCanAdd, decorateKeys, decorateMethodWithKeyName } from './decorators';
import { KeyNameType } from './utils';

export default class OverriddenAttributor extends Attributor {
constructor(attrName, keyName, options = { allowedTags: [] }) {
super(attrName, keyName, options);

this.allowedTags = options.allowedTags ?? [];
}

static keys(node) {
return decorateKeys(super.keys, node, KeyNameType.attribute);
}

add(node, value) {
return decorateMethodWithKeyName.call(this, super.add, node, value);
}

remove(node) {
return decorateMethodWithKeyName.call(this, super.remove, node);
}

value(node) {
return decorateMethodWithKeyName.call(this, super.value, node);
}

canAdd(node, value) {
return decorateCanAdd.call(this, super.canAdd, node, value);
}
}
24 changes: 24 additions & 0 deletions attributors/decorators.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import {
getKeyNameWithCustomPrefix,
removeCustomPrefixFromKeyName,
} from './utils';

export function decorateMethodWithKeyName(method, ...args) {
const originalKeyName = this.keyName;
this.keyName = removeCustomPrefixFromKeyName(this.keyName);

const result = method.call(this, ...args);

this.keyName = originalKeyName;
return result;
}

export function decorateCanAdd(originCanAdd, node, value) {
const isNodeAllowed = this.allowedTags.indexOf(node.tagName) > -1;
return isNodeAllowed && originCanAdd.call(this, node, value);
}

export function decorateKeys(originKeys, node, keyType) {
return originKeys(node)
.map((keyName) => getKeyNameWithCustomPrefix(node.tagName, keyName, keyType));
}
17 changes: 0 additions & 17 deletions attributors/element_attributor.js

This file was deleted.

17 changes: 0 additions & 17 deletions attributors/element_class.js

This file was deleted.

17 changes: 0 additions & 17 deletions attributors/element_style.js

This file was deleted.

31 changes: 31 additions & 0 deletions attributors/style_attributor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { StyleAttributor } from 'parchment';
import { decorateCanAdd, decorateKeys, decorateMethodWithKeyName } from './decorators';
import { KeyNameType } from './utils';

export default class OverriddenStyleAttributor extends StyleAttributor {
constructor(attrName, keyName, options = { allowedTags: [] }) {
super(attrName, keyName, options);

this.allowedTags = options.allowedTags ?? [];
}

static keys(node) {
return decorateKeys(super.keys, node, KeyNameType.style);
}

add(node, value) {
return decorateMethodWithKeyName.call(this, super.add, node, value);
}

remove(node) {
return decorateMethodWithKeyName.call(this, super.remove, node);
}

value(node) {
return decorateMethodWithKeyName.call(this, super.value, node);
}

canAdd(node, value) {
return decorateCanAdd.call(this, super.canAdd, node, value);
}
}
38 changes: 38 additions & 0 deletions attributors/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { TABLE_KEY_NAME_SET, tableConfig } from '../formats/table/attributors/table_config';
import { cellConfig, TABLE_CELL_KEY_NAME_SET } from '../formats/table/attributors/cell_config';

export const KeyNameType = {
attribute: 'attr',
style: 'style',
};

const OVERRIDDEN_ATTRIBUTORS_TAG_INFO = {
...tableConfig.allowedTags.reduce((result, tag) => {
result[tag] = {
name: tableConfig.name,
keyNamesSet: TABLE_KEY_NAME_SET,
};
return result;
}, {}),
...cellConfig.allowedTags.reduce((result, tag) => {
result[tag] = {
name: cellConfig.name,
keyNamesSet: TABLE_CELL_KEY_NAME_SET,
};
return result;
}, {}),
};

export function getKeyNameWithCustomPrefix(tagName, keyName, keyType) {
const tagInfo = OVERRIDDEN_ATTRIBUTORS_TAG_INFO[tagName];

if (!tagInfo) {
return keyName;
}

return tagInfo.keyNamesSet.has(keyName) ? `${keyType}${tagInfo.name}_${keyName}` : keyName;
}

export function removeCustomPrefixFromKeyName(keyNameWithPrefix) {
return keyNameWithPrefix.replace(/([^]*_)/, '');
}
3 changes: 3 additions & 0 deletions blots/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import {
import Break from './break';
import Inline from './inline';
import TextBlot from './text';
import { overrideParchment } from '../parchment/override';

overrideParchment();

const NEWLINE_LENGTH = 1;

Expand Down
133 changes: 15 additions & 118 deletions formats/table/attributors/cell.js
Original file line number Diff line number Diff line change
@@ -1,125 +1,22 @@
import prepareAttributor from './prepare_attributor';
import prepareStyleAttributor from './prepare_style_attributor';
import { cellConfig, TABLE_CELL_ATTRIBUTES, TABLE_CELL_STYLES } from './cell_config';

const cellConfig = {
name: 'cell',
allowedTags: ['TH', 'TD'],
};
export const TABLE_CELL_ATTR_ATTRIBUTORS = TABLE_CELL_ATTRIBUTES
.map((attrName) => prepareAttributor(cellConfig, attrName));

const CellHeightAttribute = prepareAttributor(cellConfig, 'height');
const CellWidthAttribute = prepareAttributor(cellConfig, 'width');
const CellHeightStyle = prepareStyleAttributor(cellConfig, 'height');
const CellWidthStyle = prepareStyleAttributor(cellConfig, 'width');
export const TABLE_CELL_STYLE_ATTRIBUTORS = TABLE_CELL_STYLES
.map((styleName) => prepareStyleAttributor(cellConfig, styleName));

const CellVerticalAlignStyle = prepareStyleAttributor(
cellConfig,
'vertical',
'align',
);

const CellTextAlignStyle = prepareStyleAttributor(cellConfig, 'text', 'align');

const CellBackgroundColorStyle = prepareStyleAttributor(
cellConfig,
'background',
'color',
);

const CellBorderStyle = prepareStyleAttributor(cellConfig, 'border');
const CellBorderStyleStyle = prepareStyleAttributor(
cellConfig,
'border',
'style',
);
const CellBorderWidthStyle = prepareStyleAttributor(
cellConfig,
'border',
'width',
);
const CellBorderColorStyle = prepareStyleAttributor(
cellConfig,
'border',
'color',
);

const CellPaddingStyle = prepareStyleAttributor(cellConfig, 'padding');
const CellPaddingTopStyle = prepareStyleAttributor(
cellConfig,
'padding',
'top',
);
const CellPaddingBottomStyle = prepareStyleAttributor(
cellConfig,
'padding',
'bottom',
);
const CellPaddingLeftStyle = prepareStyleAttributor(
cellConfig,
'padding',
'left',
);
const CellPaddingRightStyle = prepareStyleAttributor(
cellConfig,
'padding',
'right',
);

const CELL_FORMATS = {
cellBorder: CellBorderStyle,
cellBorderStyle: CellBorderStyleStyle,
cellBorderWidth: CellBorderWidthStyle,
cellBorderColor: CellBorderColorStyle,
cellBackgroundColor: CellBackgroundColorStyle,
cellPadding: CellPaddingStyle,
cellPaddingTop: CellPaddingTopStyle,
cellPaddingBottom: CellPaddingBottomStyle,
cellPaddingLeft: CellPaddingLeftStyle,
cellPaddingRight: CellPaddingRightStyle,
cellVerticalAlign: CellVerticalAlignStyle,
cellTextAlign: CellTextAlignStyle,
cellWidth: CellWidthStyle,
cellHeight: CellHeightStyle,
};

const CELL_ATTRIBUTORS = [
CellBackgroundColorStyle,
CellBorderColorStyle,
CellBorderStyle,
CellBorderStyleStyle,
CellBorderWidthStyle,
CellPaddingBottomStyle,
CellPaddingLeftStyle,
CellPaddingRightStyle,
CellPaddingStyle,
CellPaddingTopStyle,
CellVerticalAlignStyle,
CellTextAlignStyle,
CellHeightStyle,
CellWidthStyle,
CellWidthAttribute,
CellHeightAttribute,
].reduce((memo, attr) => {
memo[attr.keyName] = attr;
return memo;
export const CELL_FORMATS = TABLE_CELL_STYLE_ATTRIBUTORS.reduce((result, attributor) => {
result[attributor.attrName] = attributor;
return result;
}, {});

export {
CellVerticalAlignStyle,
CellTextAlignStyle,
CellBackgroundColorStyle,
CellBorderStyle,
CellBorderStyleStyle,
CellBorderWidthStyle,
CellBorderColorStyle,
CellPaddingStyle,
CellPaddingTopStyle,
CellPaddingBottomStyle,
CellPaddingLeftStyle,
CellPaddingRightStyle,
CellHeightStyle,
CellWidthStyle,
CellHeightAttribute,
CellWidthAttribute,
CELL_FORMATS,
CELL_ATTRIBUTORS,
};
export const CELL_ATTRIBUTORS = [
...TABLE_CELL_ATTR_ATTRIBUTORS,
...TABLE_CELL_STYLE_ATTRIBUTORS,
].reduce((result, attributor) => {
result[attributor.keyName] = attributor;
return result;
}, {});
31 changes: 31 additions & 0 deletions formats/table/attributors/cell_config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
export const cellConfig = {
name: 'cell',
allowedTags: ['TH', 'TD'],
};

export const TABLE_CELL_ATTRIBUTES = [
'height',
'width',
];

export const TABLE_CELL_STYLES = [
'height',
'width',
'vertical-align',
'text-align',
'background-color',
'border',
'border-style',
'border-width',
'border-color',
'padding',
'padding-top',
'padding-right',
'padding-bottom',
'padding-left',
];

export const TABLE_CELL_KEY_NAME_SET = new Set([
...TABLE_CELL_ATTRIBUTES,
...TABLE_CELL_STYLES,
]);
14 changes: 7 additions & 7 deletions formats/table/attributors/prepare_attributor.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import ElementAttributor from '../../../attributors/element_attributor';
import OverriddenAttributor from '../../../attributors/attributor';
import capitalize from '../../../utils/capitalize';
import { KeyNameType } from '../../../attributors/utils';

export default function prepareAttributor(
{ name, ...elementConfig },
attrName,
domAttrName,
) {
return new ElementAttributor(
`${name}${capitalize(attrName)}`,
attrName,
elementConfig,
);
const attrName = `${name}${capitalize(domAttrName)}`;
const keyName = `${KeyNameType.attribute}${name}_${domAttrName}`;

return new OverriddenAttributor(attrName, keyName, elementConfig);
}
Loading