This commit is contained in:
100
node_modules/decap-cms-widget-markdown/src/MarkdownControl/RawEditor.js
generated
vendored
Normal file
100
node_modules/decap-cms-widget-markdown/src/MarkdownControl/RawEditor.js
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { ClassNames } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { lengths, fonts } from 'decap-cms-ui-default';
|
||||
import { createEditor } from 'slate';
|
||||
import { Editable, ReactEditor, Slate, withReact } from 'slate-react';
|
||||
import { withHistory } from 'slate-history';
|
||||
|
||||
import { editorStyleVars, EditorControlBar } from '../styles';
|
||||
import Toolbar from './Toolbar';
|
||||
import defaultEmptyBlock from './plugins/blocks/defaultEmptyBlock';
|
||||
|
||||
function rawEditorStyles({ minimal }) {
|
||||
return `
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
overflow-x: auto;
|
||||
min-height: ${minimal ? 'auto' : lengths.richTextEditorMinHeight};
|
||||
font-family: ${fonts.mono};
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
border-top: 0;
|
||||
margin-top: -${editorStyleVars.stickyDistanceBottom};
|
||||
`;
|
||||
}
|
||||
|
||||
const RawEditorContainer = styled.div`
|
||||
position: relative;
|
||||
`;
|
||||
function RawEditor(props) {
|
||||
const { className, field, isShowModeToggle, t, onChange } = props;
|
||||
|
||||
const editor = useMemo(() => withReact(withHistory(createEditor())), []);
|
||||
|
||||
const [value, setValue] = useState(
|
||||
props.value
|
||||
? props.value.split('\n').map(line => defaultEmptyBlock(line))
|
||||
: [defaultEmptyBlock()],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (props.pendingFocus) {
|
||||
ReactEditor.focus(editor);
|
||||
}
|
||||
}, []);
|
||||
|
||||
function handleToggleMode() {
|
||||
props.onMode('rich_text');
|
||||
}
|
||||
|
||||
function handleChange(value) {
|
||||
onChange(value.map(line => line.children[0].text).join('\n'));
|
||||
setValue(value);
|
||||
}
|
||||
|
||||
return (
|
||||
<Slate editor={editor} value={value} onChange={handleChange}>
|
||||
<RawEditorContainer>
|
||||
<EditorControlBar>
|
||||
<Toolbar
|
||||
onToggleMode={handleToggleMode}
|
||||
buttons={field.get('buttons')}
|
||||
disabled
|
||||
rawMode
|
||||
isShowModeToggle={isShowModeToggle}
|
||||
t={t}
|
||||
/>
|
||||
</EditorControlBar>
|
||||
<ClassNames>
|
||||
{({ css, cx }) => (
|
||||
<Editable
|
||||
className={cx(
|
||||
className,
|
||||
css`
|
||||
${rawEditorStyles({ minimal: field.get('minimal') })}
|
||||
`,
|
||||
)}
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
)}
|
||||
</ClassNames>
|
||||
</RawEditorContainer>
|
||||
</Slate>
|
||||
);
|
||||
}
|
||||
|
||||
RawEditor.propTypes = {
|
||||
onChange: PropTypes.func.isRequired,
|
||||
onMode: PropTypes.func.isRequired,
|
||||
className: PropTypes.string.isRequired,
|
||||
value: PropTypes.string,
|
||||
field: ImmutablePropTypes.map.isRequired,
|
||||
isShowModeToggle: PropTypes.bool.isRequired,
|
||||
t: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default RawEditor;
|
||||
278
node_modules/decap-cms-widget-markdown/src/MarkdownControl/Toolbar.js
generated
vendored
Normal file
278
node_modules/decap-cms-widget-markdown/src/MarkdownControl/Toolbar.js
generated
vendored
Normal file
@@ -0,0 +1,278 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import styled from '@emotion/styled';
|
||||
import { css } from '@emotion/react';
|
||||
import { List } from 'immutable';
|
||||
import {
|
||||
Toggle,
|
||||
Dropdown,
|
||||
DropdownItem,
|
||||
DropdownButton,
|
||||
colors,
|
||||
transitions,
|
||||
lengths,
|
||||
} from 'decap-cms-ui-default';
|
||||
|
||||
import ToolbarButton from './ToolbarButton';
|
||||
|
||||
const ToolbarContainer = styled.div`
|
||||
background-color: ${colors.textFieldBorder};
|
||||
border-top-right-radius: ${lengths.borderRadius};
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 11px 14px;
|
||||
min-height: 58px;
|
||||
transition: background-color ${transitions.main}, color ${transitions.main};
|
||||
color: ${colors.text};
|
||||
`;
|
||||
|
||||
const ToolbarDropdownWrapper = styled.div`
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
const ToolbarToggle = styled.div`
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
margin: 0 10px;
|
||||
`;
|
||||
|
||||
const StyledToggle = ToolbarToggle.withComponent(Toggle);
|
||||
|
||||
const ToolbarToggleLabel = styled.span`
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
line-height: 20px;
|
||||
min-width: ${props => (props.offPosition ? '62px' : '70px')};
|
||||
|
||||
${props =>
|
||||
props.isActive &&
|
||||
css`
|
||||
font-weight: 600;
|
||||
color: ${colors.active};
|
||||
`};
|
||||
`;
|
||||
|
||||
export default class Toolbar extends React.Component {
|
||||
static propTypes = {
|
||||
buttons: ImmutablePropTypes.list,
|
||||
editorComponents: ImmutablePropTypes.list,
|
||||
onToggleMode: PropTypes.func.isRequired,
|
||||
rawMode: PropTypes.bool,
|
||||
isShowModeToggle: PropTypes.bool.isRequired,
|
||||
plugins: ImmutablePropTypes.map,
|
||||
onSubmit: PropTypes.func,
|
||||
onAddAsset: PropTypes.func,
|
||||
getAsset: PropTypes.func,
|
||||
disabled: PropTypes.bool,
|
||||
onMarkClick: PropTypes.func,
|
||||
onBlockClick: PropTypes.func,
|
||||
onLinkClick: PropTypes.func,
|
||||
hasMark: PropTypes.func,
|
||||
hasInline: PropTypes.func,
|
||||
hasBlock: PropTypes.func,
|
||||
t: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
isVisible = button => {
|
||||
const { buttons } = this.props;
|
||||
return !List.isList(buttons) || buttons.includes(button);
|
||||
};
|
||||
|
||||
handleBlockClick = (event, type) => {
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
this.props.onBlockClick(type);
|
||||
};
|
||||
|
||||
handleMarkClick = (event, type) => {
|
||||
event.preventDefault();
|
||||
this.props.onMarkClick(type);
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
onLinkClick,
|
||||
onToggleMode,
|
||||
rawMode,
|
||||
isShowModeToggle,
|
||||
plugins,
|
||||
disabled,
|
||||
onSubmit,
|
||||
hasMark = () => {},
|
||||
hasInline = () => {},
|
||||
hasBlock = () => {},
|
||||
hasQuote = () => {},
|
||||
hasListItems = () => {},
|
||||
editorComponents,
|
||||
t,
|
||||
} = this.props;
|
||||
const isVisible = this.isVisible;
|
||||
const showEditorComponents = !editorComponents || editorComponents.size >= 1;
|
||||
|
||||
function showPlugin({ id }) {
|
||||
return editorComponents ? editorComponents.includes(id) : true;
|
||||
}
|
||||
|
||||
const pluginsList = plugins ? plugins.toList().filter(showPlugin) : List();
|
||||
|
||||
const headingOptions = {
|
||||
'heading-one': t('editor.editorWidgets.headingOptions.headingOne'),
|
||||
'heading-two': t('editor.editorWidgets.headingOptions.headingTwo'),
|
||||
'heading-three': t('editor.editorWidgets.headingOptions.headingThree'),
|
||||
'heading-four': t('editor.editorWidgets.headingOptions.headingFour'),
|
||||
'heading-five': t('editor.editorWidgets.headingOptions.headingFive'),
|
||||
'heading-six': t('editor.editorWidgets.headingOptions.headingSix'),
|
||||
};
|
||||
|
||||
return (
|
||||
<ToolbarContainer>
|
||||
<div>
|
||||
{isVisible('bold') && (
|
||||
<ToolbarButton
|
||||
type="bold"
|
||||
label={t('editor.editorWidgets.markdown.bold')}
|
||||
icon="bold"
|
||||
onClick={this.handleMarkClick}
|
||||
isActive={hasMark('bold')}
|
||||
disabled={disabled}
|
||||
/>
|
||||
)}
|
||||
{isVisible('italic') && (
|
||||
<ToolbarButton
|
||||
type="italic"
|
||||
label={t('editor.editorWidgets.markdown.italic')}
|
||||
icon="italic"
|
||||
onClick={this.handleMarkClick}
|
||||
isActive={hasMark('italic')}
|
||||
disabled={disabled}
|
||||
/>
|
||||
)}
|
||||
{isVisible('code') && (
|
||||
<ToolbarButton
|
||||
type="code"
|
||||
label={t('editor.editorWidgets.markdown.code')}
|
||||
icon="code"
|
||||
onClick={this.handleMarkClick}
|
||||
isActive={hasMark('code')}
|
||||
disabled={disabled}
|
||||
/>
|
||||
)}
|
||||
{isVisible('link') && (
|
||||
<ToolbarButton
|
||||
type="link"
|
||||
label={t('editor.editorWidgets.markdown.link')}
|
||||
icon="link"
|
||||
onClick={onLinkClick}
|
||||
isActive={hasInline('link')}
|
||||
disabled={disabled}
|
||||
/>
|
||||
)}
|
||||
{/* Show dropdown if at least one heading is not hidden */}
|
||||
{Object.keys(headingOptions).some(isVisible) && (
|
||||
<ToolbarDropdownWrapper>
|
||||
<Dropdown
|
||||
dropdownWidth="max-content"
|
||||
dropdownTopOverlap="36px"
|
||||
renderButton={() => (
|
||||
<DropdownButton>
|
||||
<ToolbarButton
|
||||
type="headings"
|
||||
label={t('editor.editorWidgets.markdown.headings')}
|
||||
icon="hOptions"
|
||||
disabled={disabled}
|
||||
isActive={!disabled && Object.keys(headingOptions).some(hasBlock)}
|
||||
/>
|
||||
</DropdownButton>
|
||||
)}
|
||||
>
|
||||
{!disabled &&
|
||||
Object.keys(headingOptions).map(
|
||||
(optionKey, idx) =>
|
||||
isVisible(optionKey) && (
|
||||
<DropdownItem
|
||||
key={idx}
|
||||
label={headingOptions[optionKey]}
|
||||
className={hasBlock(optionKey) ? 'active' : ''}
|
||||
onClick={() => this.handleBlockClick(null, optionKey)}
|
||||
/>
|
||||
),
|
||||
)}
|
||||
</Dropdown>
|
||||
</ToolbarDropdownWrapper>
|
||||
)}
|
||||
{isVisible('quote') && (
|
||||
<ToolbarButton
|
||||
type="quote"
|
||||
label={t('editor.editorWidgets.markdown.quote')}
|
||||
icon="quote"
|
||||
onClick={this.handleBlockClick}
|
||||
isActive={hasQuote('quote')}
|
||||
disabled={disabled}
|
||||
/>
|
||||
)}
|
||||
{isVisible('bulleted-list') && (
|
||||
<ToolbarButton
|
||||
type="bulleted-list"
|
||||
label={t('editor.editorWidgets.markdown.bulletedList')}
|
||||
icon="list-bulleted"
|
||||
onClick={this.handleBlockClick}
|
||||
isActive={hasListItems('bulleted-list')}
|
||||
disabled={disabled}
|
||||
/>
|
||||
)}
|
||||
{isVisible('numbered-list') && (
|
||||
<ToolbarButton
|
||||
type="numbered-list"
|
||||
label={t('editor.editorWidgets.markdown.numberedList')}
|
||||
icon="list-numbered"
|
||||
onClick={this.handleBlockClick}
|
||||
isActive={hasListItems('numbered-list')}
|
||||
disabled={disabled}
|
||||
/>
|
||||
)}
|
||||
{showEditorComponents && (
|
||||
<ToolbarDropdownWrapper>
|
||||
<Dropdown
|
||||
dropdownTopOverlap="36px"
|
||||
dropdownWidth="max-content"
|
||||
renderButton={() => (
|
||||
<DropdownButton>
|
||||
<ToolbarButton
|
||||
label={t('editor.editorWidgets.markdown.addComponent')}
|
||||
icon="add-with"
|
||||
onClick={this.handleComponentsMenuToggle}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</DropdownButton>
|
||||
)}
|
||||
>
|
||||
{pluginsList.map((plugin, idx) => (
|
||||
<DropdownItem key={idx} label={plugin.label} onClick={() => onSubmit(plugin)} />
|
||||
))}
|
||||
</Dropdown>
|
||||
</ToolbarDropdownWrapper>
|
||||
)}
|
||||
</div>
|
||||
{isShowModeToggle && (
|
||||
<ToolbarToggle>
|
||||
<ToolbarToggleLabel isActive={!rawMode} offPosition>
|
||||
{t('editor.editorWidgets.markdown.richText')}
|
||||
</ToolbarToggleLabel>
|
||||
<StyledToggle active={rawMode} onChange={onToggleMode} />
|
||||
<ToolbarToggleLabel isActive={rawMode}>
|
||||
{t('editor.editorWidgets.markdown.markdown')}
|
||||
</ToolbarToggleLabel>
|
||||
</ToolbarToggle>
|
||||
)}
|
||||
</ToolbarContainer>
|
||||
);
|
||||
}
|
||||
}
|
||||
48
node_modules/decap-cms-widget-markdown/src/MarkdownControl/ToolbarButton.js
generated
vendored
Normal file
48
node_modules/decap-cms-widget-markdown/src/MarkdownControl/ToolbarButton.js
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styled from '@emotion/styled';
|
||||
import { Icon, buttons } from 'decap-cms-ui-default';
|
||||
|
||||
const StyledToolbarButton = styled.button`
|
||||
${buttons.button};
|
||||
display: inline-block;
|
||||
padding: 6px;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
font-size: 16px;
|
||||
color: ${props => (props.isActive ? '#1e2532' : 'inherit')};
|
||||
cursor: pointer;
|
||||
|
||||
&:disabled {
|
||||
cursor: auto;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
${Icon} {
|
||||
display: block;
|
||||
}
|
||||
`;
|
||||
|
||||
function ToolbarButton({ type, label, icon, onClick, isActive, disabled }) {
|
||||
return (
|
||||
<StyledToolbarButton
|
||||
isActive={isActive}
|
||||
onClick={e => onClick && onClick(e, type)}
|
||||
title={label}
|
||||
disabled={disabled}
|
||||
>
|
||||
{icon ? <Icon type={icon} /> : label}
|
||||
</StyledToolbarButton>
|
||||
);
|
||||
}
|
||||
|
||||
ToolbarButton.propTypes = {
|
||||
type: PropTypes.string,
|
||||
label: PropTypes.string.isRequired,
|
||||
icon: PropTypes.string,
|
||||
onClick: PropTypes.func,
|
||||
isActive: PropTypes.bool,
|
||||
disabled: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default ToolbarButton;
|
||||
293
node_modules/decap-cms-widget-markdown/src/MarkdownControl/VisualEditor.js
generated
vendored
Normal file
293
node_modules/decap-cms-widget-markdown/src/MarkdownControl/VisualEditor.js
generated
vendored
Normal file
@@ -0,0 +1,293 @@
|
||||
// @refresh reset
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { ClassNames, css as coreCss } from '@emotion/react';
|
||||
import { lengths, fonts, zIndex } from 'decap-cms-ui-default';
|
||||
import styled from '@emotion/styled';
|
||||
import { createEditor, Transforms, Editor as SlateEditor } from 'slate';
|
||||
import { Editable, ReactEditor, Slate, withReact } from 'slate-react';
|
||||
import { withHistory } from 'slate-history';
|
||||
import { fromJS } from 'immutable';
|
||||
import { isEqual } from 'lodash';
|
||||
|
||||
import { editorStyleVars, EditorControlBar } from '../styles';
|
||||
import Toolbar from './Toolbar';
|
||||
import { Element, Leaf } from './renderers';
|
||||
import withLists from './plugins/lists/withLists';
|
||||
import withBlocks from './plugins/blocks/withBlocks';
|
||||
import withInlines from './plugins/inlines/withInlines';
|
||||
import toggleMark from './plugins/inlines/events/toggleMark';
|
||||
import toggleLink from './plugins/inlines/events/toggleLink';
|
||||
import getActiveLink from './plugins/inlines/selectors/getActiveLink';
|
||||
import isMarkActive from './plugins/inlines/locations/isMarkActive';
|
||||
import isCursorInBlockType from './plugins/blocks/locations/isCursorInBlockType';
|
||||
import { markdownToSlate, slateToMarkdown } from '../serializers';
|
||||
import withShortcodes from './plugins/shortcodes/withShortcodes';
|
||||
import insertShortcode from './plugins/shortcodes/insertShortcode';
|
||||
import defaultEmptyBlock from './plugins/blocks/defaultEmptyBlock';
|
||||
|
||||
function visualEditorStyles({ minimal }) {
|
||||
return `
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
font-family: ${fonts.primary};
|
||||
min-height: ${minimal ? 'auto' : lengths.richTextEditorMinHeight};
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
border-top: 0;
|
||||
margin-top: -${editorStyleVars.stickyDistanceBottom};
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
z-index: ${zIndex.zIndex100};
|
||||
`;
|
||||
}
|
||||
|
||||
const InsertionPoint = styled.div`
|
||||
flex: 1 1 auto;
|
||||
cursor: text;
|
||||
`;
|
||||
|
||||
export function mergeMediaConfig(editorComponents, field) {
|
||||
// merge editor media library config to image components
|
||||
if (editorComponents.has('image')) {
|
||||
const imageComponent = editorComponents.get('image');
|
||||
const fields = imageComponent?.fields;
|
||||
|
||||
if (fields) {
|
||||
imageComponent.fields = fields.update(
|
||||
fields.findIndex(f => f.get('widget') === 'image'),
|
||||
f => {
|
||||
// merge `media_library` config
|
||||
if (field.has('media_library')) {
|
||||
f = f.set(
|
||||
'media_library',
|
||||
field.get('media_library').mergeDeep(f.get('media_library')),
|
||||
);
|
||||
}
|
||||
// merge 'media_folder'
|
||||
if (field.has('media_folder') && !f.has('media_folder')) {
|
||||
f = f.set('media_folder', field.get('media_folder'));
|
||||
}
|
||||
// merge 'public_folder'
|
||||
if (field.has('public_folder') && !f.has('public_folder')) {
|
||||
f = f.set('public_folder', field.get('public_folder'));
|
||||
}
|
||||
return f;
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Editor(props) {
|
||||
const {
|
||||
onAddAsset,
|
||||
getAsset,
|
||||
className,
|
||||
field,
|
||||
isShowModeToggle,
|
||||
t,
|
||||
isDisabled,
|
||||
getEditorComponents,
|
||||
getRemarkPlugins,
|
||||
onChange,
|
||||
} = props;
|
||||
|
||||
const editor = useMemo(
|
||||
() =>
|
||||
withReact(withHistory(withShortcodes(withBlocks(withLists(withInlines(createEditor())))))),
|
||||
[],
|
||||
);
|
||||
|
||||
const emptyValue = [defaultEmptyBlock()];
|
||||
let editorComponents = getEditorComponents();
|
||||
const codeBlockComponent = fromJS(editorComponents.find(({ type }) => type === 'code-block'));
|
||||
|
||||
editorComponents =
|
||||
codeBlockComponent || editorComponents.has('code-block')
|
||||
? editorComponents
|
||||
: editorComponents.set('code-block', { label: 'Code Block', type: 'code-block' });
|
||||
|
||||
mergeMediaConfig(editorComponents, field);
|
||||
|
||||
const [editorValue, setEditorValue] = useState(
|
||||
props.value
|
||||
? markdownToSlate(props.value, {
|
||||
voidCodeBlock: !!codeBlockComponent,
|
||||
remarkPlugins: getRemarkPlugins(),
|
||||
})
|
||||
: emptyValue,
|
||||
);
|
||||
|
||||
const renderElement = useCallback(
|
||||
props => (
|
||||
<Element {...props} classNameWrapper={className} codeBlockComponent={codeBlockComponent} />
|
||||
),
|
||||
[],
|
||||
);
|
||||
const renderLeaf = useCallback(props => <Leaf {...props} />, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (props.pendingFocus) {
|
||||
ReactEditor.focus(editor);
|
||||
}
|
||||
}, []);
|
||||
|
||||
function handleMarkClick(format) {
|
||||
ReactEditor.focus(editor);
|
||||
toggleMark(editor, format);
|
||||
}
|
||||
|
||||
function handleBlockClick(format) {
|
||||
ReactEditor.focus(editor);
|
||||
if (format.endsWith('-list')) {
|
||||
editor.toggleList(format);
|
||||
} else {
|
||||
editor.toggleBlock(format);
|
||||
}
|
||||
}
|
||||
|
||||
function handleLinkClick() {
|
||||
toggleLink(editor, t('editor.editorWidgets.markdown.linkPrompt'));
|
||||
ReactEditor.focus(editor);
|
||||
}
|
||||
|
||||
function handleToggleMode() {
|
||||
props.onMode('raw');
|
||||
}
|
||||
|
||||
function handleInsertShortcode(pluginConfig) {
|
||||
insertShortcode(editor, pluginConfig);
|
||||
}
|
||||
|
||||
function handleKeyDown(event) {
|
||||
for (const handler of editor.keyDownHandlers || []) {
|
||||
if (handler(event, editor) === false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ReactEditor.focus(editor);
|
||||
}
|
||||
|
||||
function handleClickBelowDocument() {
|
||||
ReactEditor.focus(editor);
|
||||
Transforms.select(editor, { path: [0, 0], offset: 0 });
|
||||
Transforms.select(editor, SlateEditor.end(editor, []));
|
||||
}
|
||||
const [toolbarKey, setToolbarKey] = useState(0);
|
||||
|
||||
function handleChange(newValue) {
|
||||
if (!isEqual(newValue, editorValue)) {
|
||||
setEditorValue(() => newValue);
|
||||
onChange(
|
||||
slateToMarkdown(newValue, {
|
||||
voidCodeBlock: !!codeBlockComponent,
|
||||
remarkPlugins: getRemarkPlugins(),
|
||||
}),
|
||||
);
|
||||
}
|
||||
setToolbarKey(prev => prev + 1);
|
||||
}
|
||||
|
||||
function hasMark(format) {
|
||||
return isMarkActive(editor, format);
|
||||
}
|
||||
|
||||
function hasInline(format) {
|
||||
if (format == 'link') {
|
||||
return !!getActiveLink(editor);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function hasBlock(format) {
|
||||
return isCursorInBlockType(editor, format);
|
||||
}
|
||||
function hasQuote() {
|
||||
return isCursorInBlockType(editor, 'quote');
|
||||
}
|
||||
function hasListItems(type) {
|
||||
return isCursorInBlockType(editor, type);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
css={coreCss`
|
||||
position: relative;
|
||||
`}
|
||||
>
|
||||
<Slate editor={editor} value={editorValue} onChange={handleChange}>
|
||||
<EditorControlBar>
|
||||
{
|
||||
<Toolbar
|
||||
key={toolbarKey}
|
||||
onMarkClick={handleMarkClick}
|
||||
onBlockClick={handleBlockClick}
|
||||
onLinkClick={handleLinkClick}
|
||||
onToggleMode={handleToggleMode}
|
||||
plugins={editorComponents}
|
||||
onSubmit={handleInsertShortcode}
|
||||
onAddAsset={onAddAsset}
|
||||
getAsset={getAsset}
|
||||
buttons={field.get('buttons')}
|
||||
editorComponents={field.get('editor_components')}
|
||||
hasMark={hasMark}
|
||||
hasInline={hasInline}
|
||||
hasBlock={hasBlock}
|
||||
hasQuote={hasQuote}
|
||||
hasListItems={hasListItems}
|
||||
isShowModeToggle={isShowModeToggle}
|
||||
t={t}
|
||||
disabled={isDisabled}
|
||||
/>
|
||||
}
|
||||
</EditorControlBar>
|
||||
{
|
||||
<ClassNames>
|
||||
{({ css, cx }) => (
|
||||
<div
|
||||
className={cx(
|
||||
className,
|
||||
css`
|
||||
${visualEditorStyles({ minimal: field.get('minimal') })}
|
||||
`,
|
||||
)}
|
||||
>
|
||||
{editorValue.length !== 0 && (
|
||||
<Editable
|
||||
className={css`
|
||||
padding: 16px 20px 0;
|
||||
`}
|
||||
renderElement={renderElement}
|
||||
renderLeaf={renderLeaf}
|
||||
onKeyDown={handleKeyDown}
|
||||
autoFocus={false}
|
||||
/>
|
||||
)}
|
||||
<InsertionPoint onClick={handleClickBelowDocument} />
|
||||
</div>
|
||||
)}
|
||||
</ClassNames>
|
||||
}
|
||||
</Slate>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Editor.propTypes = {
|
||||
onAddAsset: PropTypes.func.isRequired,
|
||||
getAsset: PropTypes.func.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
onMode: PropTypes.func.isRequired,
|
||||
className: PropTypes.string.isRequired,
|
||||
value: PropTypes.string,
|
||||
field: ImmutablePropTypes.map.isRequired,
|
||||
getEditorComponents: PropTypes.func.isRequired,
|
||||
getRemarkPlugins: PropTypes.func.isRequired,
|
||||
isShowModeToggle: PropTypes.bool.isRequired,
|
||||
t: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default Editor;
|
||||
56
node_modules/decap-cms-widget-markdown/src/MarkdownControl/__tests__/VisualEditor.spec.js
generated
vendored
Normal file
56
node_modules/decap-cms-widget-markdown/src/MarkdownControl/__tests__/VisualEditor.spec.js
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
import { Map, fromJS } from 'immutable';
|
||||
|
||||
import { mergeMediaConfig } from '../VisualEditor';
|
||||
|
||||
describe('VisualEditor', () => {
|
||||
describe('mergeMediaConfig', () => {
|
||||
it('should copy editor media settings to image component', () => {
|
||||
const editorComponents = Map({
|
||||
image: {
|
||||
id: 'image',
|
||||
label: 'Image',
|
||||
type: 'shortcode',
|
||||
icon: 'exclamation-triangle',
|
||||
widget: 'object',
|
||||
pattern: {},
|
||||
fields: fromJS([
|
||||
{
|
||||
label: 'Image',
|
||||
name: 'image',
|
||||
widget: 'image',
|
||||
media_library: { allow_multiple: false },
|
||||
},
|
||||
{ label: 'Alt Text', name: 'alt' },
|
||||
{ label: 'Title', name: 'title' },
|
||||
]),
|
||||
},
|
||||
});
|
||||
|
||||
const field = fromJS({
|
||||
label: 'Body',
|
||||
name: 'body',
|
||||
widget: 'markdown',
|
||||
media_folder: '/{{media_folder}}/posts/images/widget/body',
|
||||
public_folder: '{{public_folder}}/posts/images/widget/body',
|
||||
media_library: { config: { max_file_size: 1234 } },
|
||||
});
|
||||
|
||||
mergeMediaConfig(editorComponents, field);
|
||||
|
||||
expect(editorComponents.get('image').fields).toEqual(
|
||||
fromJS([
|
||||
{
|
||||
label: 'Image',
|
||||
name: 'image',
|
||||
widget: 'image',
|
||||
media_library: { allow_multiple: false, config: { max_file_size: 1234 } },
|
||||
media_folder: '/{{media_folder}}/posts/images/widget/body',
|
||||
public_folder: '{{public_folder}}/posts/images/widget/body',
|
||||
},
|
||||
{ label: 'Alt Text', name: 'alt' },
|
||||
{ label: 'Title', name: 'title' },
|
||||
]),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
543
node_modules/decap-cms-widget-markdown/src/MarkdownControl/__tests__/__snapshots__/parser.spec.js.snap
generated
vendored
Normal file
543
node_modules/decap-cms-widget-markdown/src/MarkdownControl/__tests__/__snapshots__/parser.spec.js.snap
generated
vendored
Normal file
@@ -0,0 +1,543 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Compile markdown to Slate Raw AST should compile kitchen sink example 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "An exhibit of Markdown",
|
||||
},
|
||||
],
|
||||
"type": "heading-one",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "This note demonstrates some of what Markdown is capable of doing.",
|
||||
},
|
||||
],
|
||||
"type": "paragraph",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"italic": true,
|
||||
"marks": Array [
|
||||
Object {
|
||||
"type": "italic",
|
||||
},
|
||||
],
|
||||
"text": "Note: Feel free to play with this page. Unlike regular notes, this doesn't
|
||||
automatically save itself.",
|
||||
},
|
||||
],
|
||||
"type": "paragraph",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "Basic formatting",
|
||||
},
|
||||
],
|
||||
"type": "heading-two",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "Paragraphs can be written like so. A paragraph is the basic block of Markdown.
|
||||
A paragraph is what text will turn into when there is no reason it should
|
||||
become anything else.",
|
||||
},
|
||||
],
|
||||
"type": "paragraph",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "Paragraphs must be separated by a blank line. Basic formatting of ",
|
||||
},
|
||||
Object {
|
||||
"italic": true,
|
||||
"marks": Array [
|
||||
Object {
|
||||
"type": "italic",
|
||||
},
|
||||
],
|
||||
"text": "italics",
|
||||
},
|
||||
Object {
|
||||
"text": " and
|
||||
",
|
||||
},
|
||||
Object {
|
||||
"bold": true,
|
||||
"marks": Array [
|
||||
Object {
|
||||
"type": "bold",
|
||||
},
|
||||
],
|
||||
"text": "bold",
|
||||
},
|
||||
Object {
|
||||
"text": " is supported. This ",
|
||||
},
|
||||
Object {
|
||||
"italic": true,
|
||||
"marks": Array [
|
||||
Object {
|
||||
"type": "italic",
|
||||
},
|
||||
],
|
||||
"text": "can be ",
|
||||
},
|
||||
Object {
|
||||
"bold": true,
|
||||
"italic": true,
|
||||
"marks": Array [
|
||||
Object {
|
||||
"type": "italic",
|
||||
},
|
||||
Object {
|
||||
"type": "bold",
|
||||
},
|
||||
],
|
||||
"text": "nested",
|
||||
},
|
||||
Object {
|
||||
"italic": true,
|
||||
"marks": Array [
|
||||
Object {
|
||||
"type": "italic",
|
||||
},
|
||||
],
|
||||
"text": " like",
|
||||
},
|
||||
Object {
|
||||
"text": " so.",
|
||||
},
|
||||
],
|
||||
"type": "paragraph",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "Lists",
|
||||
},
|
||||
],
|
||||
"type": "heading-two",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "Ordered list",
|
||||
},
|
||||
],
|
||||
"type": "heading-three",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "Item 1 2. A second item 3. Number 3 4. Ⅳ",
|
||||
},
|
||||
],
|
||||
"type": "paragraph",
|
||||
},
|
||||
],
|
||||
"type": "list-item",
|
||||
},
|
||||
],
|
||||
"data": Object {
|
||||
"start": 1,
|
||||
},
|
||||
"type": "numbered-list",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"italic": true,
|
||||
"marks": Array [
|
||||
Object {
|
||||
"type": "italic",
|
||||
},
|
||||
],
|
||||
"text": "Note: the fourth item uses the Unicode character for Roman numeral four.",
|
||||
},
|
||||
],
|
||||
"type": "paragraph",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "Unordered list",
|
||||
},
|
||||
],
|
||||
"type": "heading-three",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "An item Another item Yet another item And there's more...",
|
||||
},
|
||||
],
|
||||
"type": "paragraph",
|
||||
},
|
||||
],
|
||||
"type": "list-item",
|
||||
},
|
||||
],
|
||||
"data": Object {
|
||||
"start": null,
|
||||
},
|
||||
"type": "bulleted-list",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "Paragraph modifiers",
|
||||
},
|
||||
],
|
||||
"type": "heading-two",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "Code block",
|
||||
},
|
||||
],
|
||||
"type": "heading-three",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "Code blocks are very useful for developers and other people who look at
|
||||
code or other things that are written in plain text. As you can see, it
|
||||
uses a fixed-width font.",
|
||||
},
|
||||
],
|
||||
"data": Object {
|
||||
"lang": null,
|
||||
"shortcode": "code-block",
|
||||
"shortcodeData": Object {
|
||||
"code": "Code blocks are very useful for developers and other people who look at
|
||||
code or other things that are written in plain text. As you can see, it
|
||||
uses a fixed-width font.",
|
||||
"lang": null,
|
||||
},
|
||||
},
|
||||
"type": "shortcode",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "You can also make ",
|
||||
},
|
||||
Object {
|
||||
"code": true,
|
||||
"marks": Array [
|
||||
Object {
|
||||
"type": "code",
|
||||
},
|
||||
],
|
||||
"text": "inline code",
|
||||
},
|
||||
Object {
|
||||
"text": " to add code into other things.",
|
||||
},
|
||||
],
|
||||
"type": "paragraph",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "Quote",
|
||||
},
|
||||
],
|
||||
"type": "heading-three",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "Here is a quote. What this is should be self explanatory. Quotes are
|
||||
automatically indented when they are used.",
|
||||
},
|
||||
],
|
||||
"type": "paragraph",
|
||||
},
|
||||
],
|
||||
"type": "quote",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "Headings",
|
||||
},
|
||||
],
|
||||
"type": "heading-two",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "There are six levels of headings. They correspond with the six levels of HTML
|
||||
headings. You've probably noticed them already in the page. Each level down
|
||||
uses one more hash character.",
|
||||
},
|
||||
],
|
||||
"type": "paragraph",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "Headings ",
|
||||
},
|
||||
Object {
|
||||
"italic": true,
|
||||
"marks": Array [
|
||||
Object {
|
||||
"type": "italic",
|
||||
},
|
||||
],
|
||||
"text": "can",
|
||||
},
|
||||
Object {
|
||||
"text": " also contain ",
|
||||
},
|
||||
Object {
|
||||
"bold": true,
|
||||
"marks": Array [
|
||||
Object {
|
||||
"type": "bold",
|
||||
},
|
||||
],
|
||||
"text": "formatting",
|
||||
},
|
||||
],
|
||||
"type": "heading-three",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "They can even contain ",
|
||||
},
|
||||
Object {
|
||||
"code": true,
|
||||
"marks": Array [
|
||||
Object {
|
||||
"type": "code",
|
||||
},
|
||||
],
|
||||
"text": "inline code",
|
||||
},
|
||||
],
|
||||
"type": "heading-three",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "Of course, demonstrating what headings look like messes up the structure of the
|
||||
page.",
|
||||
},
|
||||
],
|
||||
"type": "paragraph",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "I don't recommend using more than three or four levels of headings here,
|
||||
because, when you're smallest heading isn't too small, and you're largest
|
||||
heading isn't too big, and you want each size up to look noticeably larger and
|
||||
more important, there there are only so many sizes that you can use.",
|
||||
},
|
||||
],
|
||||
"type": "paragraph",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "URLs",
|
||||
},
|
||||
],
|
||||
"type": "heading-two",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "URLs can be made in a handful of ways:",
|
||||
},
|
||||
],
|
||||
"type": "paragraph",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "A named link to MarkItDown. The easiest way to do these is to select what you",
|
||||
},
|
||||
],
|
||||
"type": "paragraph",
|
||||
},
|
||||
],
|
||||
"type": "list-item",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "want to make a link and hit ",
|
||||
},
|
||||
Object {
|
||||
"code": true,
|
||||
"marks": Array [
|
||||
Object {
|
||||
"type": "code",
|
||||
},
|
||||
],
|
||||
"text": "Ctrl+L",
|
||||
},
|
||||
Object {
|
||||
"text": ". Another named link to",
|
||||
},
|
||||
],
|
||||
"type": "paragraph",
|
||||
},
|
||||
],
|
||||
"type": "list-item",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "MarkItDown",
|
||||
},
|
||||
],
|
||||
"data": Object {
|
||||
"title": null,
|
||||
"url": "http://www.markitdown.net/",
|
||||
},
|
||||
"type": "link",
|
||||
},
|
||||
Object {
|
||||
"text": " Sometimes you just want a URL like",
|
||||
},
|
||||
],
|
||||
"type": "paragraph",
|
||||
},
|
||||
],
|
||||
"type": "list-item",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "http://www.markitdown.net/",
|
||||
},
|
||||
],
|
||||
"data": Object {
|
||||
"title": null,
|
||||
"url": "http://www.markitdown.net/",
|
||||
},
|
||||
"type": "link",
|
||||
},
|
||||
Object {
|
||||
"text": ".",
|
||||
},
|
||||
],
|
||||
"type": "paragraph",
|
||||
},
|
||||
],
|
||||
"type": "list-item",
|
||||
},
|
||||
],
|
||||
"data": Object {
|
||||
"start": null,
|
||||
},
|
||||
"type": "bulleted-list",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "Horizontal rule",
|
||||
},
|
||||
],
|
||||
"type": "heading-two",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "A horizontal rule is a line that goes across the middle of the page.",
|
||||
},
|
||||
],
|
||||
"type": "paragraph",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "",
|
||||
},
|
||||
],
|
||||
"type": "thematic-break",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "It's sometimes handy for breaking things up.",
|
||||
},
|
||||
],
|
||||
"type": "paragraph",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "Images",
|
||||
},
|
||||
],
|
||||
"type": "heading-two",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "Markdown can also contain images. I'll need to add something here sometime.",
|
||||
},
|
||||
],
|
||||
"type": "paragraph",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "Finally",
|
||||
},
|
||||
],
|
||||
"type": "heading-two",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "There's actually a lot more to Markdown than this. See the official
|
||||
introduction and syntax for more information. However, be aware that this is
|
||||
not using the official implementation, and this might work subtly differently
|
||||
in some of the little things.",
|
||||
},
|
||||
],
|
||||
"type": "paragraph",
|
||||
},
|
||||
]
|
||||
`;
|
||||
668
node_modules/decap-cms-widget-markdown/src/MarkdownControl/__tests__/parser.spec.js
generated
vendored
Normal file
668
node_modules/decap-cms-widget-markdown/src/MarkdownControl/__tests__/parser.spec.js
generated
vendored
Normal file
@@ -0,0 +1,668 @@
|
||||
import { markdownToSlate } from '../../serializers';
|
||||
|
||||
const parser = markdownToSlate;
|
||||
|
||||
describe('Compile markdown to Slate Raw AST', () => {
|
||||
it('should compile simple markdown', () => {
|
||||
const value = `
|
||||
# H1
|
||||
|
||||
sweet body
|
||||
`;
|
||||
expect(parser(value)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "H1",
|
||||
},
|
||||
],
|
||||
"type": "heading-one",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "sweet body",
|
||||
},
|
||||
],
|
||||
"type": "paragraph",
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('should compile a markdown ordered list', () => {
|
||||
const value = `
|
||||
# H1
|
||||
|
||||
1. yo
|
||||
2. bro
|
||||
3. fro
|
||||
`;
|
||||
expect(parser(value)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "H1",
|
||||
},
|
||||
],
|
||||
"type": "heading-one",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "yo",
|
||||
},
|
||||
],
|
||||
"type": "paragraph",
|
||||
},
|
||||
],
|
||||
"type": "list-item",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "bro",
|
||||
},
|
||||
],
|
||||
"type": "paragraph",
|
||||
},
|
||||
],
|
||||
"type": "list-item",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "fro",
|
||||
},
|
||||
],
|
||||
"type": "paragraph",
|
||||
},
|
||||
],
|
||||
"type": "list-item",
|
||||
},
|
||||
],
|
||||
"data": Object {
|
||||
"start": 1,
|
||||
},
|
||||
"type": "numbered-list",
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('should compile bulleted lists', () => {
|
||||
const value = `
|
||||
# H1
|
||||
|
||||
* yo
|
||||
* bro
|
||||
* fro
|
||||
`;
|
||||
expect(parser(value)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "H1",
|
||||
},
|
||||
],
|
||||
"type": "heading-one",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "yo",
|
||||
},
|
||||
],
|
||||
"type": "paragraph",
|
||||
},
|
||||
],
|
||||
"type": "list-item",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "bro",
|
||||
},
|
||||
],
|
||||
"type": "paragraph",
|
||||
},
|
||||
],
|
||||
"type": "list-item",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "fro",
|
||||
},
|
||||
],
|
||||
"type": "paragraph",
|
||||
},
|
||||
],
|
||||
"type": "list-item",
|
||||
},
|
||||
],
|
||||
"data": Object {
|
||||
"start": null,
|
||||
},
|
||||
"type": "bulleted-list",
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('should compile multiple header levels', () => {
|
||||
const value = `
|
||||
# H1
|
||||
|
||||
## H2
|
||||
|
||||
### H3
|
||||
`;
|
||||
expect(parser(value)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "H1",
|
||||
},
|
||||
],
|
||||
"type": "heading-one",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "H2",
|
||||
},
|
||||
],
|
||||
"type": "heading-two",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "H3",
|
||||
},
|
||||
],
|
||||
"type": "heading-three",
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('should compile horizontal rules', () => {
|
||||
const value = `
|
||||
# H1
|
||||
|
||||
---
|
||||
|
||||
blue moon
|
||||
`;
|
||||
expect(parser(value)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "H1",
|
||||
},
|
||||
],
|
||||
"type": "heading-one",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "",
|
||||
},
|
||||
],
|
||||
"type": "thematic-break",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "blue moon",
|
||||
},
|
||||
],
|
||||
"type": "paragraph",
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('should compile horizontal rules', () => {
|
||||
const value = `
|
||||
# H1
|
||||
|
||||
---
|
||||
|
||||
blue moon
|
||||
`;
|
||||
expect(parser(value)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "H1",
|
||||
},
|
||||
],
|
||||
"type": "heading-one",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "",
|
||||
},
|
||||
],
|
||||
"type": "thematic-break",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "blue moon",
|
||||
},
|
||||
],
|
||||
"type": "paragraph",
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('should compile soft breaks (double space)', () => {
|
||||
const value = `
|
||||
blue moon
|
||||
footballs
|
||||
`;
|
||||
expect(parser(value)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "blue moon",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "",
|
||||
},
|
||||
],
|
||||
"data": undefined,
|
||||
"type": "break",
|
||||
},
|
||||
Object {
|
||||
"text": "footballs",
|
||||
},
|
||||
],
|
||||
"type": "paragraph",
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('should compile images', () => {
|
||||
const value = `
|
||||

|
||||
`;
|
||||
expect(parser(value)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "",
|
||||
},
|
||||
],
|
||||
"data": Object {
|
||||
"alt": "super",
|
||||
"title": null,
|
||||
"url": "duper.jpg",
|
||||
},
|
||||
"type": "image",
|
||||
},
|
||||
],
|
||||
"type": "paragraph",
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('should compile code blocks', () => {
|
||||
const value = `
|
||||
\`\`\`javascript
|
||||
var a = 1;
|
||||
\`\`\`
|
||||
`;
|
||||
expect(parser(value)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "var a = 1;",
|
||||
},
|
||||
],
|
||||
"data": Object {
|
||||
"lang": "javascript",
|
||||
"shortcode": "code-block",
|
||||
"shortcodeData": Object {
|
||||
"code": "var a = 1;",
|
||||
"lang": "javascript",
|
||||
},
|
||||
},
|
||||
"type": "shortcode",
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('should compile nested inline markup', () => {
|
||||
const value = `
|
||||
# Word
|
||||
|
||||
This is **some *hot* content**
|
||||
|
||||
perhaps **scalding** even
|
||||
`;
|
||||
expect(parser(value)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "Word",
|
||||
},
|
||||
],
|
||||
"type": "heading-one",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "This is ",
|
||||
},
|
||||
Object {
|
||||
"bold": true,
|
||||
"marks": Array [
|
||||
Object {
|
||||
"type": "bold",
|
||||
},
|
||||
],
|
||||
"text": "some ",
|
||||
},
|
||||
Object {
|
||||
"bold": true,
|
||||
"italic": true,
|
||||
"marks": Array [
|
||||
Object {
|
||||
"type": "bold",
|
||||
},
|
||||
Object {
|
||||
"type": "italic",
|
||||
},
|
||||
],
|
||||
"text": "hot",
|
||||
},
|
||||
Object {
|
||||
"bold": true,
|
||||
"marks": Array [
|
||||
Object {
|
||||
"type": "bold",
|
||||
},
|
||||
],
|
||||
"text": " content",
|
||||
},
|
||||
],
|
||||
"type": "paragraph",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "perhaps ",
|
||||
},
|
||||
Object {
|
||||
"bold": true,
|
||||
"marks": Array [
|
||||
Object {
|
||||
"type": "bold",
|
||||
},
|
||||
],
|
||||
"text": "scalding",
|
||||
},
|
||||
Object {
|
||||
"text": " even",
|
||||
},
|
||||
],
|
||||
"type": "paragraph",
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('should compile inline code', () => {
|
||||
const value = `
|
||||
# Word
|
||||
|
||||
This is some sweet \`inline code\` yo!
|
||||
`;
|
||||
expect(parser(value)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "Word",
|
||||
},
|
||||
],
|
||||
"type": "heading-one",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "This is some sweet ",
|
||||
},
|
||||
Object {
|
||||
"code": true,
|
||||
"marks": Array [
|
||||
Object {
|
||||
"type": "code",
|
||||
},
|
||||
],
|
||||
"text": "inline code",
|
||||
},
|
||||
Object {
|
||||
"text": " yo!",
|
||||
},
|
||||
],
|
||||
"type": "paragraph",
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('should compile links', () => {
|
||||
const value = `
|
||||
# Word
|
||||
|
||||
How far is it to [Google](https://google.com) land?
|
||||
`;
|
||||
expect(parser(value)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "Word",
|
||||
},
|
||||
],
|
||||
"type": "heading-one",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "How far is it to ",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "Google",
|
||||
},
|
||||
],
|
||||
"data": Object {
|
||||
"title": null,
|
||||
"url": "https://google.com",
|
||||
},
|
||||
"type": "link",
|
||||
},
|
||||
Object {
|
||||
"text": " land?",
|
||||
},
|
||||
],
|
||||
"type": "paragraph",
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('should compile plugins', () => {
|
||||
const value = `
|
||||

|
||||
|
||||
{{< test >}}
|
||||
`;
|
||||
expect(parser(value)).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "",
|
||||
},
|
||||
],
|
||||
"data": Object {
|
||||
"alt": "test",
|
||||
"title": null,
|
||||
"url": "test.png",
|
||||
},
|
||||
"type": "image",
|
||||
},
|
||||
],
|
||||
"type": "paragraph",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"text": "{{< test >}}",
|
||||
},
|
||||
],
|
||||
"type": "paragraph",
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('should compile kitchen sink example', () => {
|
||||
const value = `
|
||||
# An exhibit of Markdown
|
||||
|
||||
This note demonstrates some of what Markdown is capable of doing.
|
||||
|
||||
*Note: Feel free to play with this page. Unlike regular notes, this doesn't
|
||||
automatically save itself.*
|
||||
|
||||
## Basic formatting
|
||||
|
||||
Paragraphs can be written like so. A paragraph is the basic block of Markdown.
|
||||
A paragraph is what text will turn into when there is no reason it should
|
||||
become anything else.
|
||||
|
||||
Paragraphs must be separated by a blank line. Basic formatting of *italics* and
|
||||
**bold** is supported. This *can be **nested** like* so.
|
||||
|
||||
## Lists
|
||||
|
||||
### Ordered list
|
||||
|
||||
1. Item 1 2. A second item 3. Number 3 4. Ⅳ
|
||||
|
||||
*Note: the fourth item uses the Unicode character for Roman numeral four.*
|
||||
|
||||
### Unordered list
|
||||
|
||||
* An item Another item Yet another item And there's more...
|
||||
|
||||
## Paragraph modifiers
|
||||
|
||||
### Code block
|
||||
|
||||
Code blocks are very useful for developers and other people who look at
|
||||
code or other things that are written in plain text. As you can see, it
|
||||
uses a fixed-width font.
|
||||
|
||||
You can also make \`inline code\` to add code into other things.
|
||||
|
||||
### Quote
|
||||
|
||||
> Here is a quote. What this is should be self explanatory. Quotes are
|
||||
automatically indented when they are used.
|
||||
|
||||
## Headings
|
||||
|
||||
There are six levels of headings. They correspond with the six levels of HTML
|
||||
headings. You've probably noticed them already in the page. Each level down
|
||||
uses one more hash character.
|
||||
|
||||
### Headings *can* also contain **formatting**
|
||||
|
||||
### They can even contain \`inline code\`
|
||||
|
||||
Of course, demonstrating what headings look like messes up the structure of the
|
||||
page.
|
||||
|
||||
I don't recommend using more than three or four levels of headings here,
|
||||
because, when you're smallest heading isn't too small, and you're largest
|
||||
heading isn't too big, and you want each size up to look noticeably larger and
|
||||
more important, there there are only so many sizes that you can use.
|
||||
|
||||
## URLs
|
||||
|
||||
URLs can be made in a handful of ways:
|
||||
|
||||
* A named link to MarkItDown. The easiest way to do these is to select what you
|
||||
* want to make a link and hit \`Ctrl+L\`. Another named link to
|
||||
* [MarkItDown](http://www.markitdown.net/) Sometimes you just want a URL like
|
||||
* <http://www.markitdown.net/>.
|
||||
|
||||
## Horizontal rule
|
||||
|
||||
A horizontal rule is a line that goes across the middle of the page.
|
||||
|
||||
---
|
||||
|
||||
It's sometimes handy for breaking things up.
|
||||
|
||||
## Images
|
||||
|
||||
Markdown can also contain images. I'll need to add something here sometime.
|
||||
|
||||
## Finally
|
||||
|
||||
There's actually a lot more to Markdown than this. See the official
|
||||
introduction and syntax for more information. However, be aware that this is
|
||||
not using the official implementation, and this might work subtly differently
|
||||
in some of the little things.
|
||||
`;
|
||||
expect(parser(value)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
74
node_modules/decap-cms-widget-markdown/src/MarkdownControl/components/Shortcode.js
generated
vendored
Normal file
74
node_modules/decap-cms-widget-markdown/src/MarkdownControl/components/Shortcode.js
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import React, { useState } from 'react';
|
||||
import { css } from '@emotion/react';
|
||||
import { fromJS } from 'immutable';
|
||||
import { omit } from 'lodash';
|
||||
import { ReactEditor, useSlate } from 'slate-react';
|
||||
import { Range, Transforms } from 'slate';
|
||||
|
||||
import { getEditorControl, getEditorComponents } from '../index';
|
||||
|
||||
function Shortcode(props) {
|
||||
const editor = useSlate();
|
||||
const { element, dataKey = 'shortcodeData', children } = props;
|
||||
const EditorControl = getEditorControl();
|
||||
const plugin = getEditorComponents().get(element.data.shortcode);
|
||||
const fieldKeys = ['id', 'fromBlock', 'toBlock', 'toPreview', 'pattern', 'icon'];
|
||||
|
||||
const field = fromJS(omit(plugin, fieldKeys));
|
||||
const [value, setValue] = useState(fromJS(element.data[dataKey]));
|
||||
|
||||
function handleChange(fieldName, value, metadata) {
|
||||
const path = ReactEditor.findPath(editor, element);
|
||||
const newProperties = {
|
||||
data: {
|
||||
...element.data,
|
||||
[dataKey]: value.toJS(),
|
||||
metadata,
|
||||
},
|
||||
};
|
||||
Transforms.setNodes(editor, newProperties, {
|
||||
at: path,
|
||||
});
|
||||
setValue(value);
|
||||
}
|
||||
|
||||
function handleFocus() {
|
||||
const path = ReactEditor.findPath(editor, element);
|
||||
Transforms.select(editor, path);
|
||||
}
|
||||
|
||||
const path = ReactEditor.findPath(editor, element);
|
||||
const isSelected =
|
||||
editor.selection &&
|
||||
path &&
|
||||
Range.isRange(editor.selection) &&
|
||||
Range.includes(editor.selection, path);
|
||||
|
||||
return (
|
||||
!field.isEmpty() && (
|
||||
<div onClick={handleFocus} onFocus={handleFocus}>
|
||||
<EditorControl
|
||||
css={css`
|
||||
margin-top: 0;
|
||||
margin-bottom: 16px;
|
||||
|
||||
&:first-of-type {
|
||||
margin-top: 0;
|
||||
}
|
||||
`}
|
||||
value={value}
|
||||
field={field}
|
||||
onChange={handleChange}
|
||||
isEditorComponent={true}
|
||||
onValidateObject={() => {}}
|
||||
isNewEditorComponent={element.data.shortcodeNew}
|
||||
isSelected={isSelected}
|
||||
/>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export default Shortcode;
|
||||
58
node_modules/decap-cms-widget-markdown/src/MarkdownControl/components/VoidBlock.js
generated
vendored
Normal file
58
node_modules/decap-cms-widget-markdown/src/MarkdownControl/components/VoidBlock.js
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import React from 'react';
|
||||
import { css } from '@emotion/react';
|
||||
import { zIndex } from 'decap-cms-ui-default';
|
||||
import { ReactEditor, useSlate } from 'slate-react';
|
||||
import { Transforms } from 'slate';
|
||||
|
||||
import defaultEmptyBlock from '../plugins/blocks/defaultEmptyBlock';
|
||||
|
||||
function InsertionPoint(props) {
|
||||
return (
|
||||
<div
|
||||
css={css`
|
||||
height: 32px;
|
||||
cursor: text;
|
||||
position: relative;
|
||||
z-index: ${zIndex.zIndex1};
|
||||
margin-top: -16px;
|
||||
`}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function VoidBlock({ attributes, children, element }) {
|
||||
const editor = useSlate();
|
||||
const path = ReactEditor.findPath(editor, element);
|
||||
|
||||
function insertAtPath(at) {
|
||||
Transforms.insertNodes(editor, defaultEmptyBlock(), { select: true, at });
|
||||
}
|
||||
|
||||
function handleClick(event) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
function handleInsertBefore() {
|
||||
insertAtPath(path);
|
||||
}
|
||||
|
||||
function handleInsertAfter() {
|
||||
insertAtPath([...path.slice(0, -1), path[path.length - 1] + 1]);
|
||||
}
|
||||
|
||||
const insertBefore = path[0] === 0;
|
||||
const nextElement = editor.children[path[0] + 1];
|
||||
const insertAfter = path[0] === editor.children.length - 1 || editor.isVoid(nextElement);
|
||||
|
||||
return (
|
||||
<div {...attributes} onClick={handleClick} contentEditable={false}>
|
||||
{insertBefore && <InsertionPoint onClick={handleInsertBefore} />}
|
||||
{children}
|
||||
{insertAfter && <InsertionPoint onClick={handleInsertAfter} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default VoidBlock;
|
||||
125
node_modules/decap-cms-widget-markdown/src/MarkdownControl/index.js
generated
vendored
Normal file
125
node_modules/decap-cms-widget-markdown/src/MarkdownControl/index.js
generated
vendored
Normal file
@@ -0,0 +1,125 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { List, Map } from 'immutable';
|
||||
|
||||
import RawEditor from './RawEditor';
|
||||
import VisualEditor from './VisualEditor';
|
||||
|
||||
const MODE_STORAGE_KEY = 'cms.md-mode';
|
||||
|
||||
// TODO: passing the editorControl and components like this is horrible, should
|
||||
// be handled through Redux and a separate registry store for instances
|
||||
let editorControl;
|
||||
// eslint-disable-next-line func-style
|
||||
let _getEditorComponents = () => Map();
|
||||
|
||||
export function getEditorControl() {
|
||||
return editorControl;
|
||||
}
|
||||
|
||||
export function getEditorComponents() {
|
||||
return _getEditorComponents();
|
||||
}
|
||||
|
||||
export default class MarkdownControl extends React.Component {
|
||||
static propTypes = {
|
||||
onChange: PropTypes.func.isRequired,
|
||||
onAddAsset: PropTypes.func.isRequired,
|
||||
getAsset: PropTypes.func.isRequired,
|
||||
classNameWrapper: PropTypes.string.isRequired,
|
||||
editorControl: PropTypes.elementType.isRequired,
|
||||
value: PropTypes.string,
|
||||
field: ImmutablePropTypes.map.isRequired,
|
||||
getEditorComponents: PropTypes.func,
|
||||
t: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
value: '',
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
editorControl = props.editorControl;
|
||||
const preferredMode = localStorage.getItem(MODE_STORAGE_KEY) ?? 'rich_text';
|
||||
|
||||
_getEditorComponents = props.getEditorComponents;
|
||||
this.state = {
|
||||
mode:
|
||||
this.getAllowedModes().indexOf(preferredMode) !== -1
|
||||
? preferredMode
|
||||
: this.getAllowedModes()[0],
|
||||
pendingFocus: false,
|
||||
};
|
||||
}
|
||||
|
||||
handleMode = mode => {
|
||||
this.setState({ mode, pendingFocus: true });
|
||||
localStorage.setItem(MODE_STORAGE_KEY, mode);
|
||||
};
|
||||
|
||||
processRef = ref => (this.ref = ref);
|
||||
|
||||
setFocusReceived = () => {
|
||||
this.setState({ pendingFocus: false });
|
||||
};
|
||||
|
||||
getAllowedModes = () => this.props.field.get('modes', List(['rich_text', 'raw'])).toArray();
|
||||
|
||||
render() {
|
||||
const {
|
||||
onChange,
|
||||
onAddAsset,
|
||||
getAsset,
|
||||
value,
|
||||
classNameWrapper,
|
||||
field,
|
||||
getEditorComponents,
|
||||
getRemarkPlugins,
|
||||
resolveWidget,
|
||||
t,
|
||||
isDisabled,
|
||||
} = this.props;
|
||||
|
||||
const { mode, pendingFocus } = this.state;
|
||||
const isShowModeToggle = this.getAllowedModes().length > 1;
|
||||
const visualEditor = (
|
||||
<div className="cms-editor-visual" ref={this.processRef}>
|
||||
<VisualEditor
|
||||
onChange={onChange}
|
||||
onAddAsset={onAddAsset}
|
||||
isShowModeToggle={isShowModeToggle}
|
||||
onMode={this.handleMode}
|
||||
getAsset={getAsset}
|
||||
className={classNameWrapper}
|
||||
value={value}
|
||||
field={field}
|
||||
getEditorComponents={getEditorComponents}
|
||||
getRemarkPlugins={getRemarkPlugins}
|
||||
resolveWidget={resolveWidget}
|
||||
pendingFocus={pendingFocus && this.setFocusReceived}
|
||||
t={t}
|
||||
isDisabled={isDisabled}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
const rawEditor = (
|
||||
<div className="cms-editor-raw" ref={this.processRef}>
|
||||
<RawEditor
|
||||
onChange={onChange}
|
||||
onAddAsset={onAddAsset}
|
||||
isShowModeToggle={isShowModeToggle}
|
||||
onMode={this.handleMode}
|
||||
getAsset={getAsset}
|
||||
className={classNameWrapper}
|
||||
value={value}
|
||||
field={field}
|
||||
pendingFocus={pendingFocus && this.setFocusReceived}
|
||||
t={t}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
return mode === 'rich_text' ? visualEditor : rawEditor;
|
||||
}
|
||||
}
|
||||
23
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/BreakToDefaultBlock.js
generated
vendored
Normal file
23
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/BreakToDefaultBlock.js
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
import isHotkey from 'is-hotkey';
|
||||
|
||||
function BreakToDefaultBlock({ defaultType }) {
|
||||
return {
|
||||
onKeyDown(event, editor, next) {
|
||||
const { selection, startBlock } = editor.value;
|
||||
const isEnter = isHotkey('enter', event);
|
||||
if (!isEnter) {
|
||||
return next();
|
||||
}
|
||||
if (selection.isExpanded) {
|
||||
editor.delete();
|
||||
return next();
|
||||
}
|
||||
if (selection.start.isAtEndOfNode(startBlock) && startBlock.type !== defaultType) {
|
||||
return editor.insertBlock(defaultType);
|
||||
}
|
||||
return next();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default BreakToDefaultBlock;
|
||||
25
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/CloseBlock.js
generated
vendored
Normal file
25
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/CloseBlock.js
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
import isHotkey from 'is-hotkey';
|
||||
|
||||
function CloseBlock({ defaultType }) {
|
||||
return {
|
||||
onKeyDown(event, editor, next) {
|
||||
const { selection, startBlock } = editor.value;
|
||||
const isBackspace = isHotkey('backspace', event);
|
||||
if (!isBackspace) {
|
||||
return next();
|
||||
}
|
||||
if (selection.isExpanded) {
|
||||
return editor.delete();
|
||||
}
|
||||
if (!selection.start.isAtStartOfNode(startBlock) || startBlock.text.length > 0) {
|
||||
return next();
|
||||
}
|
||||
if (startBlock.type !== defaultType) {
|
||||
editor.setBlocks(defaultType);
|
||||
}
|
||||
return next();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default CloseBlock;
|
||||
156
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/CommandsAndQueries.js
generated
vendored
Normal file
156
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/CommandsAndQueries.js
generated
vendored
Normal file
@@ -0,0 +1,156 @@
|
||||
import { isArray, tail, castArray } from 'lodash';
|
||||
|
||||
function CommandsAndQueries({ defaultType }) {
|
||||
return {
|
||||
queries: {
|
||||
atStartOf(editor, node) {
|
||||
const { selection } = editor.value;
|
||||
return selection.isCollapsed && selection.start.isAtStartOfNode(node);
|
||||
},
|
||||
getAncestor(editor, firstKey, lastKey) {
|
||||
if (firstKey === lastKey) {
|
||||
return editor.value.document.getParent(firstKey);
|
||||
}
|
||||
return editor.value.document.getCommonAncestor(firstKey, lastKey);
|
||||
},
|
||||
getOffset(editor, node) {
|
||||
const parent = editor.value.document.getParent(node.key);
|
||||
return parent.nodes.indexOf(node);
|
||||
},
|
||||
getSelectedChildren(editor, node) {
|
||||
return node.nodes.filter(child => editor.isSelected(child));
|
||||
},
|
||||
getCommonAncestor(editor) {
|
||||
const { startBlock, endBlock, document: doc } = editor.value;
|
||||
return doc.getCommonAncestor(startBlock.key, endBlock.key);
|
||||
},
|
||||
getClosestType(editor, node, type) {
|
||||
const types = castArray(type);
|
||||
return editor.value.document.getClosest(node.key, n => types.includes(n.type));
|
||||
},
|
||||
getBlockContainer(editor, node) {
|
||||
const targetTypes = ['bulleted-list', 'numbered-list', 'list-item', 'quote', 'table-cell'];
|
||||
const { startBlock, selection } = editor.value;
|
||||
const target = node
|
||||
? editor.value.document.getParent(node.key)
|
||||
: (selection.isCollapsed && startBlock) || editor.getCommonAncestor();
|
||||
if (!target) {
|
||||
return editor.value.document;
|
||||
}
|
||||
if (targetTypes.includes(target.type)) {
|
||||
return target;
|
||||
}
|
||||
return editor.getBlockContainer(target);
|
||||
},
|
||||
isSelected(editor, nodes) {
|
||||
return castArray(nodes).every(node => {
|
||||
return editor.value.document.isInRange(node.key, editor.value.selection);
|
||||
});
|
||||
},
|
||||
isFirstChild(editor, node) {
|
||||
return editor.value.document.getParent(node.key).nodes.first().key === node.key;
|
||||
},
|
||||
areSiblings(editor, nodes) {
|
||||
if (!isArray(nodes) || nodes.length < 2) {
|
||||
return true;
|
||||
}
|
||||
const parent = editor.value.document.getParent(nodes[0].key);
|
||||
return tail(nodes).every(node => {
|
||||
return editor.value.document.getParent(node.key).key === parent.key;
|
||||
});
|
||||
},
|
||||
everyBlock(editor, type) {
|
||||
return editor.value.blocks.every(block => block.type === type);
|
||||
},
|
||||
hasMark(editor, type) {
|
||||
return editor.value.activeMarks.some(mark => mark.type === type);
|
||||
},
|
||||
hasBlock(editor, type) {
|
||||
return editor.value.blocks.some(node => node.type === type);
|
||||
},
|
||||
hasInline(editor, type) {
|
||||
return editor.value.inlines.some(node => node.type === type);
|
||||
},
|
||||
hasQuote(editor, quoteLabel) {
|
||||
const { value } = editor;
|
||||
const { document, blocks } = value;
|
||||
return blocks.some(node => {
|
||||
const { key: descendantNodeKey } = node;
|
||||
/* When focusing a quote block, the actual block that gets the focus is the paragraph block whose parent is a `quote` block.
|
||||
Hence, we need to get its parent and check if its type is `quote`. This parent will always be defined because every block in the editor
|
||||
has a Document object as parent by default.
|
||||
*/
|
||||
const parent = document.getParent(descendantNodeKey);
|
||||
return parent.type === quoteLabel;
|
||||
});
|
||||
},
|
||||
hasListItems(editor, listType) {
|
||||
const { value } = editor;
|
||||
const { document, blocks } = value;
|
||||
return blocks.some(node => {
|
||||
const { key: lowestNodeKey } = node;
|
||||
/* A list block has the following structure:
|
||||
<ol>
|
||||
<li>
|
||||
<p>Coffee</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Tea</p>
|
||||
</li>
|
||||
</ol>
|
||||
*/
|
||||
const parent = document.getParent(lowestNodeKey);
|
||||
const grandparent = document.getParent(parent.key);
|
||||
return parent.type === 'list-item' && grandparent?.type === listType;
|
||||
});
|
||||
},
|
||||
},
|
||||
commands: {
|
||||
toggleBlock(editor, type) {
|
||||
switch (type) {
|
||||
case 'heading-one':
|
||||
case 'heading-two':
|
||||
case 'heading-three':
|
||||
case 'heading-four':
|
||||
case 'heading-five':
|
||||
case 'heading-six':
|
||||
return editor.setBlocks(editor.everyBlock(type) ? defaultType : type);
|
||||
case 'quote':
|
||||
return editor.toggleQuoteBlock();
|
||||
case 'numbered-list':
|
||||
case 'bulleted-list': {
|
||||
return editor.toggleList(type);
|
||||
}
|
||||
}
|
||||
},
|
||||
unwrapBlockChildren(editor, block) {
|
||||
if (!block || block.object !== 'block') {
|
||||
throw Error(`Expected block but received ${block}.`);
|
||||
}
|
||||
const index = editor.value.document.getPath(block.key).last();
|
||||
const parent = editor.value.document.getParent(block.key);
|
||||
editor.withoutNormalizing(() => {
|
||||
block.nodes.forEach((node, idx) => {
|
||||
editor.moveNodeByKey(node.key, parent.key, index + idx);
|
||||
});
|
||||
editor.removeNodeByKey(block.key);
|
||||
});
|
||||
},
|
||||
unwrapNodeToDepth(editor, node, depth) {
|
||||
let currentDepth = 0;
|
||||
editor.withoutNormalizing(() => {
|
||||
while (currentDepth < depth) {
|
||||
editor.unwrapNodeByKey(node.key);
|
||||
currentDepth += 1;
|
||||
}
|
||||
});
|
||||
},
|
||||
unwrapNodeFromAncestor(editor, node, ancestor) {
|
||||
const depth = ancestor.getDepth(node.key);
|
||||
editor.unwrapNodeToDepth(node, depth);
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default CommandsAndQueries;
|
||||
38
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/ForceInsert.js
generated
vendored
Normal file
38
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/ForceInsert.js
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
function ForceInsert({ defaultType }) {
|
||||
return {
|
||||
queries: {
|
||||
canInsertBeforeNode(editor, node) {
|
||||
if (!editor.isVoid(node)) {
|
||||
return true;
|
||||
}
|
||||
return !!editor.value.document.getPreviousSibling(node.key);
|
||||
},
|
||||
canInsertAfterNode(editor, node) {
|
||||
if (!editor.isVoid(node)) {
|
||||
return true;
|
||||
}
|
||||
const nextSibling = editor.value.document.getNextSibling(node.key);
|
||||
return nextSibling && !editor.isVoid(nextSibling);
|
||||
},
|
||||
},
|
||||
commands: {
|
||||
forceInsertBeforeNode(editor, node) {
|
||||
const block = { type: defaultType, object: 'block' };
|
||||
const parent = editor.value.document.getParent(node.key);
|
||||
return editor.insertNodeByKey(parent.key, 0, block).moveToStartOfNode(parent).focus();
|
||||
},
|
||||
forceInsertAfterNode(editor, node) {
|
||||
return editor.moveToEndOfNode(node).insertBlock(defaultType).focus();
|
||||
},
|
||||
moveToEndOfDocument(editor) {
|
||||
const lastBlock = editor.value.document.nodes.last();
|
||||
if (editor.isVoid(lastBlock)) {
|
||||
editor.insertBlock(defaultType);
|
||||
}
|
||||
return editor.moveToEndOfNode(lastBlock).focus();
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default ForceInsert;
|
||||
29
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/Hotkey.js
generated
vendored
Normal file
29
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/Hotkey.js
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
import isHotkey from 'is-hotkey';
|
||||
|
||||
export const HOT_KEY_MAP = {
|
||||
bold: 'mod+b',
|
||||
code: 'mod+shift+c',
|
||||
italic: 'mod+i',
|
||||
strikethrough: 'mod+shift+s',
|
||||
'heading-one': 'mod+1',
|
||||
'heading-two': 'mod+2',
|
||||
'heading-three': 'mod+3',
|
||||
'heading-four': 'mod+4',
|
||||
'heading-five': 'mod+5',
|
||||
'heading-six': 'mod+6',
|
||||
link: 'mod+k',
|
||||
};
|
||||
|
||||
function Hotkey(key, fn) {
|
||||
return {
|
||||
onKeyDown(event, editor, next) {
|
||||
if (!isHotkey(key, event)) {
|
||||
return next();
|
||||
}
|
||||
event.preventDefault();
|
||||
editor.command(fn);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default Hotkey;
|
||||
15
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/LineBreak.js
generated
vendored
Normal file
15
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/LineBreak.js
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
import isHotkey from 'is-hotkey';
|
||||
|
||||
function LineBreak() {
|
||||
return {
|
||||
onKeyDown(event, editor, next) {
|
||||
const isShiftEnter = isHotkey('shift+enter', event);
|
||||
if (!isShiftEnter) {
|
||||
return next();
|
||||
}
|
||||
return editor.insertInline('break').insertText('').moveToStartOfNextText();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default LineBreak;
|
||||
40
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/Link.js
generated
vendored
Normal file
40
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/Link.js
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
function Link({ type }) {
|
||||
return {
|
||||
commands: {
|
||||
toggleLink(editor, getUrl) {
|
||||
const selection = editor.value.selection;
|
||||
const isCollapsed = selection && selection.isCollapsed;
|
||||
|
||||
if (editor.hasInline(type)) {
|
||||
const inlines = editor.value.inlines.toJSON();
|
||||
const link = inlines.find(item => item.type === type);
|
||||
|
||||
const url = getUrl(link.data.url);
|
||||
|
||||
if (url) {
|
||||
// replace the old link
|
||||
return editor.setInlines({ data: { url } });
|
||||
} else {
|
||||
// remove url if it was removed by the user
|
||||
return editor.unwrapInline(type);
|
||||
}
|
||||
} else {
|
||||
const url = getUrl();
|
||||
if (!url) {
|
||||
return;
|
||||
}
|
||||
|
||||
return isCollapsed
|
||||
? editor.insertInline({
|
||||
type,
|
||||
data: { url },
|
||||
nodes: [{ object: 'text', text: url }],
|
||||
})
|
||||
: editor.wrapInline({ type, data: { url } }).moveToEnd();
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default Link;
|
||||
8
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/defaultEmptyBlock.js
generated
vendored
Normal file
8
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/defaultEmptyBlock.js
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
function defaultEmptyBlock(text = '') {
|
||||
return {
|
||||
type: 'paragraph',
|
||||
children: [{ text }],
|
||||
};
|
||||
}
|
||||
|
||||
export default defaultEmptyBlock;
|
||||
56
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/events/keyDown.js
generated
vendored
Normal file
56
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/events/keyDown.js
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
import isHotkey from 'is-hotkey';
|
||||
import { Editor, Transforms } from 'slate';
|
||||
|
||||
import keyDownEnter from './keyDownEnter';
|
||||
import keyDownBackspace from './keyDownBackspace';
|
||||
import isCursorInNonDefaultBlock from '../locations/isCursorInNonDefaultBlock';
|
||||
import toggleBlock from './toggleBlock';
|
||||
import isCursorCollapsedAfterSoftBreak from '../locations/isCursorCollapsedAfterSoftBreak';
|
||||
|
||||
const HEADING_HOTKEYS = {
|
||||
'mod+1': 'heading-one',
|
||||
'mod+2': 'heading-two',
|
||||
'mod+3': 'heading-three',
|
||||
'mod+4': 'heading-four',
|
||||
'mod+5': 'heading-five',
|
||||
'mod+6': 'heading-six',
|
||||
};
|
||||
|
||||
function keyDown(event, editor) {
|
||||
if (!editor.selection) return;
|
||||
|
||||
for (const hotkey in HEADING_HOTKEYS) {
|
||||
if (isHotkey(hotkey, event)) {
|
||||
toggleBlock(editor, HEADING_HOTKEYS[hotkey]);
|
||||
event.preventDefault();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (isHotkey('backspace', event) && isCursorCollapsedAfterSoftBreak(editor)) {
|
||||
const [, path] = Editor.previous(editor);
|
||||
Transforms.removeNodes(editor, { at: path });
|
||||
event.preventDefault();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isCursorInNonDefaultBlock(editor)) return;
|
||||
|
||||
if (isHotkey('enter', event)) {
|
||||
const eventIntercepted = keyDownEnter(editor);
|
||||
if (eventIntercepted) {
|
||||
event.preventDefault();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (isHotkey('backspace', event)) {
|
||||
const eventIntercepted = keyDownBackspace(editor);
|
||||
if (eventIntercepted) {
|
||||
event.preventDefault();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default keyDown;
|
||||
27
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/events/keyDownBackspace.js
generated
vendored
Normal file
27
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/events/keyDownBackspace.js
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
import { Transforms } from 'slate';
|
||||
|
||||
import unwrapIfCursorAtStart from '../transforms/unwrapIfCursorAtStart';
|
||||
import isCursorAtStartOfNonEmptyHeading from '../locations/isCursorAtStartOfNonEmptyHeading';
|
||||
import lowestMatchedAncestor from '../../matchers/lowestMatchedAncestor';
|
||||
import areCurrentAndPreviousBlocksOfType from '../locations/areCurrentAndPreviousBlocksOfType';
|
||||
import isCursorAtStartOfBlockType from '../locations/isCursorAtStartOfBlockType';
|
||||
|
||||
function keyDownBackspace(editor) {
|
||||
if (!editor.selection) return;
|
||||
|
||||
if (isCursorAtStartOfNonEmptyHeading(editor)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
isCursorAtStartOfBlockType(editor, 'quote') &&
|
||||
areCurrentAndPreviousBlocksOfType(editor, 'quote')
|
||||
) {
|
||||
Transforms.mergeNodes(editor, lowestMatchedAncestor(editor, 'quote'));
|
||||
return true;
|
||||
}
|
||||
|
||||
return unwrapIfCursorAtStart(editor, true);
|
||||
}
|
||||
|
||||
export default keyDownBackspace;
|
||||
26
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/events/keyDownEnter.js
generated
vendored
Normal file
26
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/events/keyDownEnter.js
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
import isCursorInBlockType from '../locations/isCursorInBlockType';
|
||||
import splitIntoParagraph from '../transforms/splitIntoParagraph';
|
||||
import unwrapIfCursorAtStart from '../transforms/unwrapIfCursorAtStart';
|
||||
import isCursorAtEndOfParagraph from '../locations/isCursorAtEndOfParagraph';
|
||||
|
||||
function keyDownEnter(editor) {
|
||||
if (!editor.selection) return;
|
||||
|
||||
if (isCursorInBlockType(editor, 'heading', true)) {
|
||||
return handleHeading(editor);
|
||||
}
|
||||
|
||||
return unwrapIfCursorAtStart(editor);
|
||||
}
|
||||
|
||||
function handleHeading(editor) {
|
||||
if (isCursorAtEndOfParagraph(editor)) {
|
||||
// split into paragraph if cursor is at the end of heading
|
||||
splitIntoParagraph(editor);
|
||||
return true;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
export default keyDownEnter;
|
||||
39
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/events/toggleBlock.js
generated
vendored
Normal file
39
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/events/toggleBlock.js
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
import { Range, Transforms } from 'slate';
|
||||
|
||||
import isCursorInBlockType from '../locations/isCursorInBlockType';
|
||||
import getListTypeAtCursor from '../locations/getListTypeAtCursor';
|
||||
import wrapListItemsInBlock from '../transforms/wrapListItemsInBlock';
|
||||
|
||||
function toggleBlock(editor, type) {
|
||||
const { selection } = editor;
|
||||
if (!selection) return;
|
||||
|
||||
const isHeading = type.startsWith('heading-');
|
||||
const isActive = isCursorInBlockType(editor, type, isHeading, Range.isExpanded(selection));
|
||||
const listType = getListTypeAtCursor(editor);
|
||||
|
||||
// headings do not contain paragraphs so they could be converted, not wrapped/unwrapped
|
||||
if (isHeading) {
|
||||
Transforms.setNodes(editor, { type: isActive ? 'paragraph' : type });
|
||||
return;
|
||||
}
|
||||
|
||||
const { focus, anchor } = selection;
|
||||
if (
|
||||
!isActive &&
|
||||
listType &&
|
||||
focus.path[focus.path.length - 3] != anchor.path[anchor.path.length - 3]
|
||||
) {
|
||||
return wrapListItemsInBlock(editor, type, listType);
|
||||
}
|
||||
|
||||
if (!isActive) {
|
||||
return Transforms.wrapNodes(editor, { type });
|
||||
}
|
||||
|
||||
Transforms.unwrapNodes(editor, { match: n => n.type === type });
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
export default toggleBlock;
|
||||
15
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/locations/areCurrentAndPreviousBlocksOfType.js
generated
vendored
Normal file
15
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/locations/areCurrentAndPreviousBlocksOfType.js
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
import { Editor } from 'slate';
|
||||
|
||||
import lowestMatchedAncestor from '../../matchers/lowestMatchedAncestor';
|
||||
|
||||
function areCurrentAndPreviousBlocksOfType(editor, type) {
|
||||
const { selection } = editor;
|
||||
if (!selection) return false;
|
||||
|
||||
const [current] = Editor.nodes(editor, lowestMatchedAncestor(editor, 'block'));
|
||||
const previous = Editor.previous(editor, lowestMatchedAncestor(editor, type));
|
||||
|
||||
return current && previous && current[0].type === previous[0].type;
|
||||
}
|
||||
|
||||
export default areCurrentAndPreviousBlocksOfType;
|
||||
11
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/locations/getListTypeAtCursor.js
generated
vendored
Normal file
11
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/locations/getListTypeAtCursor.js
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Editor } from 'slate';
|
||||
|
||||
import lowestMatchedAncestor from '../../matchers/lowestMatchedAncestor';
|
||||
|
||||
function getListTypeAtCursor(editor) {
|
||||
const list = Editor.above(editor, lowestMatchedAncestor(editor, 'list'));
|
||||
if (!list) return null;
|
||||
return list[0].type;
|
||||
}
|
||||
|
||||
export default getListTypeAtCursor;
|
||||
14
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/locations/isCursorAtEndOfParagraph.js
generated
vendored
Normal file
14
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/locations/isCursorAtEndOfParagraph.js
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
import { Editor } from 'slate';
|
||||
|
||||
import lowestMatchedAncestor from '../../matchers/lowestMatchedAncestor';
|
||||
|
||||
function isCursorAtEndOfParagraph(editor) {
|
||||
const { selection } = editor;
|
||||
if (!selection) return false;
|
||||
|
||||
const paragraph = Editor.above(editor, lowestMatchedAncestor(editor, 'paragraph'));
|
||||
|
||||
return !!paragraph && Editor.isEnd(editor, editor.selection.focus, paragraph[1]);
|
||||
}
|
||||
|
||||
export default isCursorAtEndOfParagraph;
|
||||
14
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/locations/isCursorAtStartOfBlockType.js
generated
vendored
Normal file
14
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/locations/isCursorAtStartOfBlockType.js
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
import { Editor } from 'slate';
|
||||
|
||||
import lowestMatchedAncestor from '../../matchers/lowestMatchedAncestor';
|
||||
|
||||
function isCursorAtStartOfBlockType(editor, type) {
|
||||
const { selection } = editor;
|
||||
if (!selection) return false;
|
||||
|
||||
const block = Editor.above(editor, lowestMatchedAncestor(editor, type));
|
||||
|
||||
return !!block && Editor.isStart(editor, editor.selection.focus, block[1]);
|
||||
}
|
||||
|
||||
export default isCursorAtStartOfBlockType;
|
||||
22
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/locations/isCursorAtStartOfNonEmptyHeading.js
generated
vendored
Normal file
22
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/locations/isCursorAtStartOfNonEmptyHeading.js
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Editor, Element } from 'slate';
|
||||
|
||||
function isCursorAtStartOfNonEmptyHeading(editor) {
|
||||
const { selection } = editor;
|
||||
if (!selection) return false;
|
||||
|
||||
const [match] = Array.from(
|
||||
Editor.nodes(editor, {
|
||||
match: n =>
|
||||
Element.isElement(n) && Editor.isBlock(editor, n) && `${n.type}`.startsWith('heading-'),
|
||||
mode: 'lowest',
|
||||
}),
|
||||
);
|
||||
|
||||
return (
|
||||
!!match &&
|
||||
Editor.isStart(editor, editor.selection.focus, match[1]) &&
|
||||
!Editor.isEmpty(editor, match[0])
|
||||
);
|
||||
}
|
||||
|
||||
export default isCursorAtStartOfNonEmptyHeading;
|
||||
13
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/locations/isCursorCollapsedAfterSoftBreak.js
generated
vendored
Normal file
13
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/locations/isCursorCollapsedAfterSoftBreak.js
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Editor, Range } from 'slate';
|
||||
|
||||
function isCursorCollapsedAfterSoftBreak(editor) {
|
||||
const { selection } = editor;
|
||||
if (!selection) return false;
|
||||
if (Range.isExpanded(selection)) return false;
|
||||
|
||||
const previous = Editor.previous(editor);
|
||||
|
||||
return previous && previous[0].type == 'break';
|
||||
}
|
||||
|
||||
export default isCursorCollapsedAfterSoftBreak;
|
||||
27
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/locations/isCursorInBlockType.js
generated
vendored
Normal file
27
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/locations/isCursorInBlockType.js
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
import { Editor, Element } from 'slate';
|
||||
|
||||
function isCursorInBlockType(editor, type, ignoreHeadings, ignoreLists) {
|
||||
const { selection } = editor;
|
||||
if (!selection) return false;
|
||||
|
||||
const [match] = Array.from(
|
||||
Editor.nodes(editor, {
|
||||
match: n =>
|
||||
Element.isElement(n) &&
|
||||
Editor.isBlock(editor, n) &&
|
||||
n.type !== 'paragraph' &&
|
||||
n.type !== 'list-item' &&
|
||||
(ignoreHeadings || !`${n.type}`.startsWith('heading-')) &&
|
||||
(!ignoreLists || !`${n.type}`.endsWith('-list')),
|
||||
mode: 'lowest',
|
||||
}),
|
||||
);
|
||||
|
||||
return (
|
||||
!!match &&
|
||||
(match[0].type === type ||
|
||||
`${match[0].type}`.startsWith(`${type}-` || `${match[0].type}`.endsWith(`-${type}`)))
|
||||
);
|
||||
}
|
||||
|
||||
export default isCursorInBlockType;
|
||||
17
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/locations/isCursorInNonDefaultBlock.js
generated
vendored
Normal file
17
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/locations/isCursorInNonDefaultBlock.js
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Editor, Element } from 'slate';
|
||||
|
||||
function isCursorInNonDefaultBlock(editor) {
|
||||
const { selection } = editor;
|
||||
if (!selection) return false;
|
||||
|
||||
const [match] = Array.from(
|
||||
Editor.nodes(editor, {
|
||||
match: n => Element.isElement(n) && Editor.isBlock(editor, n) && n.type !== 'paragraph',
|
||||
mode: 'lowest',
|
||||
}),
|
||||
);
|
||||
|
||||
return !!match && !Editor.isEditor(match[0]);
|
||||
}
|
||||
|
||||
export default isCursorInNonDefaultBlock;
|
||||
13
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/transforms/splitIntoParagraph.js
generated
vendored
Normal file
13
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/transforms/splitIntoParagraph.js
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Editor, Transforms } from 'slate';
|
||||
|
||||
function splitIntoParagraph(editor) {
|
||||
Editor.withoutNormalizing(editor, () => {
|
||||
Transforms.splitNodes(editor, { always: true });
|
||||
Transforms.setNodes(editor, { type: 'paragraph' });
|
||||
});
|
||||
|
||||
Editor.normalize(editor, { force: true });
|
||||
return true;
|
||||
}
|
||||
|
||||
export default splitIntoParagraph;
|
||||
43
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/transforms/unwrapIfCursorAtStart.js
generated
vendored
Normal file
43
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/transforms/unwrapIfCursorAtStart.js
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
import { Editor, Transforms } from 'slate';
|
||||
|
||||
import lowestMatchedAncestor from '../../matchers/lowestMatchedAncestor';
|
||||
|
||||
function unwrapIfCursorAtStart(editor, mergeWithPrevious = false) {
|
||||
if (editor.selection.anchor.offset !== 0) return false;
|
||||
|
||||
let [node, path] = Editor.above(editor, lowestMatchedAncestor(editor, 'non-default'));
|
||||
|
||||
if (path.length == 0) return false;
|
||||
|
||||
const isHeading = `${node.type}`.startsWith('heading-');
|
||||
if (isHeading) {
|
||||
Transforms.setNodes(editor, { type: 'paragraph' });
|
||||
return false;
|
||||
}
|
||||
|
||||
const isBlock = Editor.isBlock(editor, node);
|
||||
const [parentBlock, parentBlockPath] = Editor.above(
|
||||
editor,
|
||||
lowestMatchedAncestor(editor, 'block'),
|
||||
);
|
||||
if (!isBlock) {
|
||||
if (!Editor.isStart(editor, path, parentBlockPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
[node, path] = [parentBlock, parentBlockPath];
|
||||
}
|
||||
|
||||
Editor.withoutNormalizing(editor, () => {
|
||||
Transforms.unwrapNodes(editor, { match: n => n.type === node.type, split: true });
|
||||
|
||||
if (mergeWithPrevious) {
|
||||
Transforms.mergeNodes(editor);
|
||||
}
|
||||
});
|
||||
|
||||
Editor.normalize(editor, { force: true });
|
||||
return true;
|
||||
}
|
||||
|
||||
export default unwrapIfCursorAtStart;
|
||||
11
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/transforms/wrapListItemsInBlock.js
generated
vendored
Normal file
11
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/transforms/wrapListItemsInBlock.js
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Editor, Transforms } from 'slate';
|
||||
|
||||
function wrapListItemsInBlock(editor, blockType, listType) {
|
||||
Editor.withoutNormalizing(editor, () => {
|
||||
Transforms.wrapNodes(editor, { type: listType });
|
||||
Transforms.wrapNodes(editor, { type: blockType }, { match: n => n.type === listType });
|
||||
Transforms.liftNodes(editor, { match: n => n.type === blockType });
|
||||
});
|
||||
Editor.normalize(editor, { force: true });
|
||||
}
|
||||
export default wrapListItemsInBlock;
|
||||
15
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/withBlocks.js
generated
vendored
Normal file
15
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/withBlocks.js
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
import keyDown from './events/keyDown';
|
||||
import toggleBlock from './events/toggleBlock';
|
||||
|
||||
function withBlocks(editor) {
|
||||
if (editor.keyDownHandlers === undefined) {
|
||||
editor.keyDownHandlers = [];
|
||||
}
|
||||
editor.keyDownHandlers.push((event, editor) => keyDown(event, editor));
|
||||
|
||||
editor.toggleBlock = type => toggleBlock(editor, type);
|
||||
|
||||
return editor;
|
||||
}
|
||||
|
||||
export default withBlocks;
|
||||
38
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/inlines/events/keyDown.js
generated
vendored
Normal file
38
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/inlines/events/keyDown.js
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
import isHotkey from 'is-hotkey';
|
||||
|
||||
import toggleMark from './toggleMark';
|
||||
import keyDownShiftEnter from './keyDownShiftEnter';
|
||||
import toggleLink from './toggleLink';
|
||||
|
||||
const MARK_HOTKEYS = {
|
||||
'mod+b': 'bold',
|
||||
'mod+i': 'italic',
|
||||
'mod+u': 'underline',
|
||||
'mod+`': 'code',
|
||||
'mod+shift+s': 'delete',
|
||||
'mod+shift+c': 'code',
|
||||
};
|
||||
|
||||
function keyDown(event, editor) {
|
||||
if (!editor.selection) return;
|
||||
|
||||
for (const hotkey in MARK_HOTKEYS) {
|
||||
if (isHotkey(hotkey, event)) {
|
||||
toggleMark(editor, MARK_HOTKEYS[hotkey]);
|
||||
event.preventDefault();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (isHotkey('mod+k', event)) {
|
||||
event.preventDefault();
|
||||
return toggleLink(editor);
|
||||
}
|
||||
|
||||
if (isHotkey('shift+enter', event)) {
|
||||
event.preventDefault();
|
||||
return keyDownShiftEnter(editor);
|
||||
}
|
||||
}
|
||||
|
||||
export default keyDown;
|
||||
28
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/inlines/events/keyDownShiftEnter.js
generated
vendored
Normal file
28
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/inlines/events/keyDownShiftEnter.js
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
import { Editor, Transforms } from 'slate';
|
||||
|
||||
function keyDownShiftEnter(editor) {
|
||||
if (!editor.selection) return;
|
||||
|
||||
const focus = {
|
||||
path: [
|
||||
...editor.selection.focus.path.slice(0, -1),
|
||||
editor.selection.focus.path[editor.selection.focus.path.length - 1] + 2,
|
||||
],
|
||||
offset: 0,
|
||||
};
|
||||
|
||||
Transforms.insertNodes(editor, {
|
||||
type: 'break',
|
||||
children: [
|
||||
{
|
||||
text: '',
|
||||
},
|
||||
],
|
||||
});
|
||||
Editor.normalize(editor, { force: true });
|
||||
|
||||
Transforms.select(editor, focus);
|
||||
return false;
|
||||
}
|
||||
|
||||
export default keyDownShiftEnter;
|
||||
17
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/inlines/events/toggleLink.js
generated
vendored
Normal file
17
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/inlines/events/toggleLink.js
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
import getActiveLink from '../selectors/getActiveLink';
|
||||
import unwrapLink from '../transforms/unwrapLink';
|
||||
import wrapLink from '../transforms/wrapLink';
|
||||
|
||||
function toggleLink(editor, promptText) {
|
||||
const activeLink = getActiveLink(editor);
|
||||
const activeUrl = activeLink ? activeLink[0]?.data?.url : '';
|
||||
const url = window.prompt(promptText, activeUrl);
|
||||
if (url == null) return;
|
||||
if (url === '') {
|
||||
unwrapLink(editor);
|
||||
return;
|
||||
}
|
||||
wrapLink(editor, url);
|
||||
}
|
||||
|
||||
export default toggleLink;
|
||||
13
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/inlines/events/toggleMark.js
generated
vendored
Normal file
13
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/inlines/events/toggleMark.js
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Editor } from 'slate';
|
||||
|
||||
import isMarkActive from '../locations/isMarkActive';
|
||||
|
||||
function toggleMark(editor, format) {
|
||||
if (isMarkActive(editor, format)) {
|
||||
Editor.removeMark(editor, format);
|
||||
} else {
|
||||
Editor.addMark(editor, format, true);
|
||||
}
|
||||
}
|
||||
|
||||
export default toggleMark;
|
||||
11
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/inlines/locations/isMarkActive.js
generated
vendored
Normal file
11
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/inlines/locations/isMarkActive.js
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Editor } from 'slate';
|
||||
|
||||
function isMarkActive(editor, format) {
|
||||
const { selection } = editor;
|
||||
if (!selection) return false;
|
||||
|
||||
const marks = Editor.marks(editor);
|
||||
return marks ? marks[format] === true : false;
|
||||
}
|
||||
|
||||
export default isMarkActive;
|
||||
10
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/inlines/selectors/getActiveLink.js
generated
vendored
Normal file
10
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/inlines/selectors/getActiveLink.js
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Editor } from 'slate';
|
||||
|
||||
import matchLink from '../../matchers/matchLink';
|
||||
|
||||
function getActiveLink(editor) {
|
||||
const [link] = Editor.nodes(editor, matchLink(editor));
|
||||
return link;
|
||||
}
|
||||
|
||||
export default getActiveLink;
|
||||
9
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/inlines/transforms/unwrapLink.js
generated
vendored
Normal file
9
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/inlines/transforms/unwrapLink.js
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
import { Transforms } from 'slate';
|
||||
|
||||
import matchLink from '../../matchers/matchLink';
|
||||
|
||||
function unwrapLink(editor) {
|
||||
Transforms.unwrapNodes(editor, matchLink());
|
||||
}
|
||||
|
||||
export default unwrapLink;
|
||||
30
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/inlines/transforms/wrapLink.js
generated
vendored
Normal file
30
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/inlines/transforms/wrapLink.js
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
import { Range, Transforms } from 'slate';
|
||||
|
||||
import getActiveLink from '../selectors/getActiveLink';
|
||||
import matchLink from '../../matchers/matchLink';
|
||||
|
||||
function wrapLink(editor, url) {
|
||||
if (getActiveLink(editor)) {
|
||||
Transforms.setNodes(editor, { data: { url } }, matchLink());
|
||||
return;
|
||||
}
|
||||
|
||||
const { selection } = editor;
|
||||
const isCollapsed = selection && Range.isCollapsed(selection);
|
||||
const link = {
|
||||
type: 'link',
|
||||
data: {
|
||||
url,
|
||||
},
|
||||
children: isCollapsed ? [{ text: url }] : [],
|
||||
};
|
||||
|
||||
if (isCollapsed) {
|
||||
Transforms.insertNodes(editor, link);
|
||||
} else {
|
||||
Transforms.wrapNodes(editor, link, { split: true });
|
||||
Transforms.collapse(editor, { edge: 'end' });
|
||||
}
|
||||
}
|
||||
|
||||
export default wrapLink;
|
||||
20
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/inlines/withInlines.js
generated
vendored
Normal file
20
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/inlines/withInlines.js
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
import keyDown from './events/keyDown';
|
||||
|
||||
function withInlines(editor) {
|
||||
const { isInline, isVoid } = editor;
|
||||
|
||||
editor.isInline = element =>
|
||||
['link', 'button', 'break', 'image'].includes(element.type) || isInline(element);
|
||||
|
||||
editor.isVoid = element =>
|
||||
['break', 'image', 'thematic-break'].includes(element.type) || isVoid(element);
|
||||
|
||||
if (editor.keyDownHandlers === undefined) {
|
||||
editor.keyDownHandlers = [];
|
||||
}
|
||||
editor.keyDownHandlers.push((event, editor) => keyDown(event, editor));
|
||||
|
||||
return editor;
|
||||
}
|
||||
|
||||
export default withInlines;
|
||||
34
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/events/keyDown.js
generated
vendored
Normal file
34
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/events/keyDown.js
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
import isHotkey from 'is-hotkey';
|
||||
|
||||
import keyDownEnter from './keyDownEnter';
|
||||
import keyDownTab from './keyDownTab';
|
||||
import keyDownShiftTab from './keyDownShiftTab';
|
||||
import keyDownBackspace from './keyDownBackspace';
|
||||
|
||||
function keyDown(event, editor) {
|
||||
if (!editor.isListItem()) return;
|
||||
|
||||
if (isHotkey('enter', event)) {
|
||||
event.preventDefault();
|
||||
keyDownEnter(editor);
|
||||
return false;
|
||||
}
|
||||
if (isHotkey('backspace', event)) {
|
||||
const eventIntercepted = keyDownBackspace(editor);
|
||||
if (eventIntercepted === false) {
|
||||
event.preventDefault();
|
||||
return false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (isHotkey('tab', event)) {
|
||||
event.preventDefault();
|
||||
return keyDownTab(editor);
|
||||
}
|
||||
if (isHotkey('shift+tab', event)) {
|
||||
event.preventDefault();
|
||||
return keyDownShiftTab(editor);
|
||||
}
|
||||
}
|
||||
|
||||
export default keyDown;
|
||||
31
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/events/keyDownBackspace.js
generated
vendored
Normal file
31
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/events/keyDownBackspace.js
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
import { Range } from 'slate';
|
||||
|
||||
import isCursorInListItem from '../locations/isCursorInListItem';
|
||||
import isSelectionWithinNoninitialListItem from '../locations/isSelectionWithinNoninitialListItem';
|
||||
import unwrapSelectionFromList from '../transforms/unwrapSelectionFromList';
|
||||
import mergeWithPreviousListItem from '../transforms/mergeWithPreviousListItem';
|
||||
import isCursorAtNoninitialParagraphStart from '../locations/isCursorAtNoninitialParagraphStart';
|
||||
|
||||
function keyDownBackspace(editor) {
|
||||
if (!editor.selection) return;
|
||||
|
||||
// ignore if selection is expanded, cursor is not at the beginning or not immediately in a list item, or cursor is at the beginning of a non-initial paragraph
|
||||
if (
|
||||
!Range.isCollapsed(editor.selection) ||
|
||||
editor.selection.anchor.offset !== 0 ||
|
||||
!isCursorInListItem(editor, true) ||
|
||||
isCursorAtNoninitialParagraphStart(editor)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isSelectionWithinNoninitialListItem(editor)) {
|
||||
mergeWithPreviousListItem(editor);
|
||||
} else {
|
||||
unwrapSelectionFromList(editor);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export default keyDownBackspace;
|
||||
39
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/events/keyDownEnter.js
generated
vendored
Normal file
39
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/events/keyDownEnter.js
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
import { Range, Transforms } from 'slate';
|
||||
|
||||
import splitListItem from '../transforms/splitListItem';
|
||||
import isCursorAtListItemStart from '../locations/isCursorAtListItemStart';
|
||||
import liftListItem from '../transforms/liftListItem';
|
||||
import convertParagraphToListItem from '../transforms/convertParagraphToListItem';
|
||||
import isCursorAtNoninitialParagraphStart from '../locations/isCursorAtNoninitialParagraphStart';
|
||||
import splitToNestedList from '../transforms/splitToNestedList';
|
||||
import getListContainedInListItem from '../selectors/getListContainedInListItem';
|
||||
|
||||
function keyDownEnter(editor) {
|
||||
if (!editor.selection) return;
|
||||
|
||||
// Pressing enter will delete current selection in any case
|
||||
if (Range.isExpanded(editor.selection)) {
|
||||
Transforms.delete(editor);
|
||||
}
|
||||
|
||||
// if edge of selection is in the beginning of the first text node in list-item
|
||||
if (isCursorAtListItemStart(editor)) {
|
||||
return liftListItem(editor);
|
||||
}
|
||||
|
||||
// if list has a nested list, insert new item to the beginning of the nested list
|
||||
const nestedList = getListContainedInListItem(editor);
|
||||
if (!!nestedList && `${nestedList[0].type}`.endsWith('-list')) {
|
||||
return splitToNestedList(editor, nestedList[0].type);
|
||||
}
|
||||
|
||||
// if a paragraph in a list and has previous siblings, convert it to a list item
|
||||
if (isCursorAtNoninitialParagraphStart(editor)) {
|
||||
return convertParagraphToListItem(editor);
|
||||
}
|
||||
|
||||
// otherwise create a new list item
|
||||
splitListItem(editor);
|
||||
}
|
||||
|
||||
export default keyDownEnter;
|
||||
27
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/events/keyDownShiftTab.js
generated
vendored
Normal file
27
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/events/keyDownShiftTab.js
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
import { Editor, Transforms } from 'slate';
|
||||
|
||||
import lowestMatchedAncestor from '../../matchers/lowestMatchedAncestor';
|
||||
import matchedAncestors from '../../matchers/matchedAncestors';
|
||||
|
||||
function keyDownShiftTab(editor) {
|
||||
if (!editor.selection) return;
|
||||
|
||||
if (Array.from(Editor.nodes(editor, matchedAncestors(editor, 'list'))).length < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
Editor.withoutNormalizing(editor, () => {
|
||||
Transforms.liftNodes(editor, {
|
||||
...lowestMatchedAncestor(editor, 'list-item'),
|
||||
split: true,
|
||||
});
|
||||
Transforms.liftNodes(editor, {
|
||||
...lowestMatchedAncestor(editor, 'list-item'),
|
||||
split: true,
|
||||
});
|
||||
});
|
||||
|
||||
Editor.normalize(editor);
|
||||
}
|
||||
|
||||
export default keyDownShiftTab;
|
||||
55
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/events/keyDownTab.js
generated
vendored
Normal file
55
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/events/keyDownTab.js
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
import { Editor, Transforms } from 'slate';
|
||||
|
||||
import isSelectionWithinNoninitialListItem from '../locations/isSelectionWithinNoninitialListItem';
|
||||
import lowestMatchedAncestor from '../../matchers/lowestMatchedAncestor';
|
||||
import moveListToListItem from '../transforms/moveListToListItem';
|
||||
|
||||
function keyDownTab(editor) {
|
||||
if (!editor.selection) return;
|
||||
|
||||
if (!isSelectionWithinNoninitialListItem(editor)) return;
|
||||
|
||||
// In a case where one edge of the range is within a nested list item, we need to even the selection to the outer most level
|
||||
const { focus, anchor } = editor.selection;
|
||||
|
||||
const pathLength =
|
||||
focus.path.length > anchor.path.length ? anchor.path.length : focus.path.length;
|
||||
const at = {
|
||||
anchor: {
|
||||
offset: 0,
|
||||
path: [...anchor.path.slice(0, pathLength - 2), 0, 0],
|
||||
},
|
||||
focus: {
|
||||
offset: 0,
|
||||
path: [...focus.path.slice(0, pathLength - 2), 0, 0],
|
||||
},
|
||||
};
|
||||
|
||||
Editor.withoutNormalizing(editor, () => {
|
||||
// wrap selected list items into a new bulleted list
|
||||
Transforms.wrapNodes(
|
||||
editor,
|
||||
{
|
||||
type: 'bulleted-list',
|
||||
},
|
||||
{
|
||||
...lowestMatchedAncestor(editor, 'list-item'),
|
||||
at,
|
||||
},
|
||||
);
|
||||
|
||||
// get the new bulleted list position
|
||||
const [, newListPath] = Editor.above(editor, lowestMatchedAncestor(editor, 'list'));
|
||||
|
||||
// get the new parent node (previous list item)
|
||||
const parentNode = Editor.previous(editor, {
|
||||
at: newListPath,
|
||||
});
|
||||
|
||||
moveListToListItem(editor, newListPath, parentNode);
|
||||
});
|
||||
|
||||
Editor.normalize(editor);
|
||||
}
|
||||
|
||||
export default keyDownTab;
|
||||
23
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/events/toggleListType.js
generated
vendored
Normal file
23
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/events/toggleListType.js
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
import isCursorInListItem from '../locations/isCursorInListItem';
|
||||
import getLowestAncestorList from '../selectors/getLowestAncestorList';
|
||||
import wrapSelectionInList from '../transforms/wrapSelectionInList';
|
||||
import changeListType from '../transforms/changeListType';
|
||||
import unwrapSelectionFromList from '../transforms/unwrapSelectionFromList';
|
||||
|
||||
function toggleListType(editor, type) {
|
||||
// list being active means that we are in a paragraph or heading whose parent is a list
|
||||
// if no list is active, wrap selection in a new list of the given type
|
||||
if (!isCursorInListItem(editor)) {
|
||||
return wrapSelectionInList(editor, type);
|
||||
}
|
||||
// if a list is active but the type doesn't match, change selection to the given list type
|
||||
const currentList = getLowestAncestorList(editor);
|
||||
if (currentList && currentList[0].type !== type) {
|
||||
return changeListType(editor, type);
|
||||
}
|
||||
|
||||
// if a list is active and the type matches, unwrap selection from the list
|
||||
return unwrapSelectionFromList(editor);
|
||||
}
|
||||
|
||||
export default toggleListType;
|
||||
13
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/locations/isCursorAtListItemStart.js
generated
vendored
Normal file
13
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/locations/isCursorAtListItemStart.js
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Range } from 'slate';
|
||||
|
||||
function isCursorAtListItemStart(editor) {
|
||||
if (!editor.selection) return false;
|
||||
|
||||
const { offset, path } = Range.start(editor.selection);
|
||||
// todo: this will break when there are marks inside list items, use Edior.isStart on first block parent instead (see isCursorAtEndOfParagraph)
|
||||
return (
|
||||
offset === 0 && path.length >= 2 && path[path.length - 1] === 0 && path[path.length - 2] === 0
|
||||
);
|
||||
}
|
||||
|
||||
export default isCursorAtListItemStart;
|
||||
11
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/locations/isCursorAtNoninitialParagraphStart.js
generated
vendored
Normal file
11
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/locations/isCursorAtNoninitialParagraphStart.js
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Range } from 'slate';
|
||||
|
||||
function isCursorAtNoninitialParagraphStart(editor) {
|
||||
if (!editor.selection) return false;
|
||||
|
||||
const { offset, path } = Range.start(editor.selection);
|
||||
|
||||
return offset == 0 && path.length > 2 && path[path.length - 2] > 0;
|
||||
}
|
||||
|
||||
export default isCursorAtNoninitialParagraphStart;
|
||||
9
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/locations/isCursorInItemContainingNestedList.js
generated
vendored
Normal file
9
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/locations/isCursorInItemContainingNestedList.js
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
import getListContainedInListItem from '../selectors/getListContainedInListItem';
|
||||
|
||||
function isCursorInItemContainingNestedList(editor) {
|
||||
const nestedList = getListContainedInListItem(editor);
|
||||
|
||||
return !!nestedList && `${nestedList[0].type}`.endsWith('-list');
|
||||
}
|
||||
|
||||
export default isCursorInItemContainingNestedList;
|
||||
21
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/locations/isCursorInListItem.js
generated
vendored
Normal file
21
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/locations/isCursorInListItem.js
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
import { Editor, Element } from 'slate';
|
||||
|
||||
function isCursorInListItem(editor, immediate) {
|
||||
const { selection } = editor;
|
||||
if (!selection) return false;
|
||||
|
||||
const [match] = Array.from(
|
||||
Editor.nodes(editor, {
|
||||
match: n =>
|
||||
Element.isElement(n) &&
|
||||
Editor.isBlock(editor, n) &&
|
||||
n.type !== 'paragraph' &&
|
||||
(immediate || !`${n.type}`.startsWith('heading-')),
|
||||
mode: 'lowest',
|
||||
}),
|
||||
);
|
||||
|
||||
return !!match && match[0].type === 'list-item';
|
||||
}
|
||||
|
||||
export default isCursorInListItem;
|
||||
14
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/locations/isSelectionWithinNoninitialListItem.js
generated
vendored
Normal file
14
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/locations/isSelectionWithinNoninitialListItem.js
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
import { Editor, Range } from 'slate';
|
||||
|
||||
function isSelectionWithinNoninitialListItem(editor) {
|
||||
if (!editor.selection) return false;
|
||||
|
||||
const [, path] = Editor.above(editor, {
|
||||
match: n => n.type === 'list-item',
|
||||
mode: 'lowest',
|
||||
at: Range.start(editor.selection),
|
||||
});
|
||||
if (path && path.length > 0 && path[path.length - 1] > 0) return true;
|
||||
}
|
||||
|
||||
export default isSelectionWithinNoninitialListItem;
|
||||
12
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/selectors/getListContainedInListItem.js
generated
vendored
Normal file
12
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/selectors/getListContainedInListItem.js
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Editor } from 'slate';
|
||||
|
||||
import lowestMatchedAncestor from '../../matchers/lowestMatchedAncestor';
|
||||
|
||||
function getListContainedInListItem(editor) {
|
||||
if (!editor.selection) return false;
|
||||
|
||||
const [, paragraphPath] = Editor.above(editor, lowestMatchedAncestor(editor, 'paragraph'));
|
||||
return Editor.next(editor, { at: paragraphPath });
|
||||
}
|
||||
|
||||
export default getListContainedInListItem;
|
||||
11
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/selectors/getLowestAncestorList.js
generated
vendored
Normal file
11
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/selectors/getLowestAncestorList.js
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Editor } from 'slate';
|
||||
|
||||
import lowestMatchedAncestor from '../../matchers/lowestMatchedAncestor';
|
||||
|
||||
function getLowestAncestorList(editor) {
|
||||
if (!editor.selection) return false;
|
||||
|
||||
return Editor.above(editor, lowestMatchedAncestor(editor, 'list'));
|
||||
}
|
||||
|
||||
export default getLowestAncestorList;
|
||||
11
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/selectors/getLowestAncestorQuote.js
generated
vendored
Normal file
11
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/selectors/getLowestAncestorQuote.js
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Editor } from 'slate';
|
||||
|
||||
import lowestMatchedAncestor from '../../matchers/lowestMatchedAncestor';
|
||||
|
||||
function getLowestAncestorQuote(editor) {
|
||||
if (!editor.selection) return false;
|
||||
|
||||
return Editor.above(editor, lowestMatchedAncestor(editor, 'quote'));
|
||||
}
|
||||
|
||||
export default getLowestAncestorQuote;
|
||||
16
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/transforms/changeListType.js
generated
vendored
Normal file
16
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/transforms/changeListType.js
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Editor, Transforms } from 'slate';
|
||||
|
||||
import lowestMatchedAncestor from '../../matchers/lowestMatchedAncestor';
|
||||
|
||||
function changeListType(editor, type) {
|
||||
Editor.withoutNormalizing(editor, () => {
|
||||
// wrap selected list items into new type
|
||||
Transforms.wrapNodes(editor, { type }, lowestMatchedAncestor(editor, 'list-item'));
|
||||
// lift the new list of the current list, split if necessary
|
||||
Transforms.liftNodes(editor, lowestMatchedAncestor(editor, type));
|
||||
});
|
||||
|
||||
Editor.normalize(editor, { force: true });
|
||||
}
|
||||
|
||||
export default changeListType;
|
||||
19
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/transforms/convertParagraphToListItem.js
generated
vendored
Normal file
19
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/transforms/convertParagraphToListItem.js
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
import { Editor } from 'slate';
|
||||
|
||||
import liftFirstMatchedParent from './liftFirstMatchedParent';
|
||||
import wrapFirstMatchedParent from './wrapFirstMatchedParent';
|
||||
|
||||
function convertParagraphToListItem(editor) {
|
||||
Editor.withoutNormalizing(editor, () => {
|
||||
// wrap the paragraph in a list item
|
||||
wrapFirstMatchedParent(editor, 'paragraph', {
|
||||
type: 'list-item',
|
||||
});
|
||||
// lift the new list-item of the current list-item, split if necessary
|
||||
liftFirstMatchedParent(editor, 'list-item', { split: true });
|
||||
});
|
||||
|
||||
Editor.normalize(editor, { force: true });
|
||||
}
|
||||
|
||||
export default convertParagraphToListItem;
|
||||
11
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/transforms/liftFirstMatchedParent.js
generated
vendored
Normal file
11
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/transforms/liftFirstMatchedParent.js
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Transforms } from 'slate';
|
||||
|
||||
function liftFirstMatchedParent(editor, format, options) {
|
||||
Transforms.liftNodes(editor, {
|
||||
match: n => n.type === format || (format === 'paragraph' && `${n.type}`.startsWith('heading')),
|
||||
mode: 'lowest',
|
||||
...options,
|
||||
});
|
||||
}
|
||||
|
||||
export default liftFirstMatchedParent;
|
||||
30
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/transforms/liftListItem.js
generated
vendored
Normal file
30
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/transforms/liftListItem.js
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
import { Editor } from 'slate';
|
||||
|
||||
import unwrapFirstMatchedParent from './unwrapFirstMatchedParent';
|
||||
import liftFirstMatchedParent from './liftFirstMatchedParent';
|
||||
import getLowestAncestorList from '../selectors/getLowestAncestorList';
|
||||
import getLowestAncestorQuote from '../selectors/getLowestAncestorQuote';
|
||||
|
||||
function liftListItem(editor) {
|
||||
Editor.withoutNormalizing(editor, () => {
|
||||
// lift the paragraph out of the list and split if necessary
|
||||
liftFirstMatchedParent(editor, 'list-item', { split: true });
|
||||
|
||||
// if list is nested and not wrapped in quote, lift into the parent list, unwrap otherwise
|
||||
const parentList = getLowestAncestorList(editor);
|
||||
const parentQuote = getLowestAncestorQuote(editor);
|
||||
if (
|
||||
(parentList && !parentQuote) ||
|
||||
(parentList && parentQuote && parentList[1].length > parentQuote[1].length)
|
||||
) {
|
||||
liftFirstMatchedParent(editor, 'list-item', { split: true });
|
||||
} else {
|
||||
// unwrap the paragraph from list-item element
|
||||
unwrapFirstMatchedParent(editor, 'list-item');
|
||||
}
|
||||
});
|
||||
|
||||
Editor.normalize(editor, { force: true });
|
||||
}
|
||||
|
||||
export default liftListItem;
|
||||
13
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/transforms/mergeWithPreviousListItem.js
generated
vendored
Normal file
13
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/transforms/mergeWithPreviousListItem.js
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Editor, Transforms } from 'slate';
|
||||
|
||||
import lowestMatchedAncestor from '../../matchers/lowestMatchedAncestor';
|
||||
|
||||
function mergeWithPreviousListItem(editor) {
|
||||
Editor.withoutNormalizing(editor, () => {
|
||||
Transforms.mergeNodes(editor, lowestMatchedAncestor(editor, 'list-item'));
|
||||
});
|
||||
|
||||
Editor.normalize(editor, { force: true });
|
||||
}
|
||||
|
||||
export default mergeWithPreviousListItem;
|
||||
13
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/transforms/moveListToListItem.js
generated
vendored
Normal file
13
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/transforms/moveListToListItem.js
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Transforms } from 'slate';
|
||||
|
||||
function moveListToListItem(editor, listPath, targetListItem) {
|
||||
const [targetItem, targetPath] = targetListItem;
|
||||
|
||||
// move the node to the last child position of the parent node
|
||||
Transforms.moveNodes(editor, {
|
||||
at: listPath,
|
||||
to: [...targetPath, targetItem.children.length],
|
||||
});
|
||||
}
|
||||
|
||||
export default moveListToListItem;
|
||||
33
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/transforms/splitListItem.js
generated
vendored
Normal file
33
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/transforms/splitListItem.js
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Editor, Range, Transforms } from 'slate';
|
||||
|
||||
import liftFirstMatchedParent from './liftFirstMatchedParent';
|
||||
import wrapFirstMatchedParent from './wrapFirstMatchedParent';
|
||||
|
||||
function splitListItem(editor) {
|
||||
if (!editor.selection) return false;
|
||||
|
||||
if (Range.isExpanded(editor.selection)) {
|
||||
Transforms.delete(editor);
|
||||
}
|
||||
|
||||
Editor.withoutNormalizing(editor, () => {
|
||||
// split even if at the end of current text
|
||||
Transforms.splitNodes(editor, {
|
||||
always: true,
|
||||
});
|
||||
// set the new node to paragraph (to avoid splitting headings)
|
||||
Transforms.setNodes(editor, {
|
||||
type: 'paragraph',
|
||||
});
|
||||
// wrap the paragraph in a list item
|
||||
wrapFirstMatchedParent(editor, 'paragraph', {
|
||||
type: 'list-item',
|
||||
});
|
||||
// lift new list item out the paragraph
|
||||
liftFirstMatchedParent(editor, 'list-item');
|
||||
});
|
||||
|
||||
Editor.normalize(editor, { force: true });
|
||||
}
|
||||
|
||||
export default splitListItem;
|
||||
33
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/transforms/splitToNestedList.js
generated
vendored
Normal file
33
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/transforms/splitToNestedList.js
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Editor, Range, Transforms } from 'slate';
|
||||
|
||||
import wrapFirstMatchedParent from './wrapFirstMatchedParent';
|
||||
|
||||
function splitToNestedList(editor, listType) {
|
||||
if (!editor.selection) return false;
|
||||
|
||||
if (Range.isExpanded(editor.selection)) {
|
||||
Transforms.delete(editor);
|
||||
}
|
||||
|
||||
Editor.withoutNormalizing(editor, () => {
|
||||
// split even if at the end of current text
|
||||
Transforms.splitNodes(editor, {
|
||||
always: true,
|
||||
});
|
||||
// set the new node to paragraph (to avoid splitting headings)
|
||||
Transforms.setNodes(editor, {
|
||||
type: 'paragraph',
|
||||
});
|
||||
// wrap the paragraph in a list item
|
||||
wrapFirstMatchedParent(editor, 'paragraph', {
|
||||
type: 'list-item',
|
||||
});
|
||||
wrapFirstMatchedParent(editor, 'list-item', {
|
||||
type: listType,
|
||||
});
|
||||
});
|
||||
|
||||
Editor.normalize(editor, { force: true });
|
||||
}
|
||||
|
||||
export default splitToNestedList;
|
||||
9
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/transforms/unwrapFirstMatchedParent.js
generated
vendored
Normal file
9
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/transforms/unwrapFirstMatchedParent.js
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
import { Transforms } from 'slate';
|
||||
|
||||
import lowestMatchedAncestor from '../../matchers/lowestMatchedAncestor';
|
||||
|
||||
function unwrapFirstMatchedParent(editor, format) {
|
||||
Transforms.unwrapNodes(editor, lowestMatchedAncestor(editor, format));
|
||||
}
|
||||
|
||||
export default unwrapFirstMatchedParent;
|
||||
14
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/transforms/unwrapSelectionFromList.js
generated
vendored
Normal file
14
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/transforms/unwrapSelectionFromList.js
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
import { Editor, Transforms } from 'slate';
|
||||
|
||||
import lowestMatchedAncestor from '../../matchers/lowestMatchedAncestor';
|
||||
|
||||
function unwrapSelectionFromList(editor) {
|
||||
Editor.withoutNormalizing(editor, () => {
|
||||
Transforms.unwrapNodes(editor, { ...lowestMatchedAncestor(editor, 'list'), split: true });
|
||||
Transforms.unwrapNodes(editor, lowestMatchedAncestor(editor, 'list-item'));
|
||||
});
|
||||
|
||||
Editor.normalize(editor);
|
||||
}
|
||||
|
||||
export default unwrapSelectionFromList;
|
||||
9
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/transforms/wrapFirstMatchedParent.js
generated
vendored
Normal file
9
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/transforms/wrapFirstMatchedParent.js
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
import { Transforms } from 'slate';
|
||||
|
||||
import lowestMatchedAncestor from '../../matchers/lowestMatchedAncestor';
|
||||
|
||||
function wrapFirstMatchedParent(editor, format, node) {
|
||||
Transforms.wrapNodes(editor, node, lowestMatchedAncestor(editor, format));
|
||||
}
|
||||
|
||||
export default wrapFirstMatchedParent;
|
||||
17
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/transforms/wrapSelectionInList.js
generated
vendored
Normal file
17
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/transforms/wrapSelectionInList.js
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Editor, Transforms } from 'slate';
|
||||
|
||||
import lowestMatchedAncestor from '../../matchers/lowestMatchedAncestor';
|
||||
|
||||
function wrapSelectionInList(editor, type) {
|
||||
Editor.withoutNormalizing(editor, () => {
|
||||
Transforms.wrapNodes(editor, { type });
|
||||
const listItems = Editor.nodes(editor, lowestMatchedAncestor(editor, 'paragraph'));
|
||||
for (const listItem of listItems) {
|
||||
Transforms.wrapNodes(editor, { type: 'list-item' }, { at: listItem[1] });
|
||||
}
|
||||
});
|
||||
|
||||
Editor.normalize(editor);
|
||||
}
|
||||
|
||||
export default wrapSelectionInList;
|
||||
66
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/withLists.js
generated
vendored
Normal file
66
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/lists/withLists.js
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
import { Editor, Element, Node, Transforms } from 'slate';
|
||||
|
||||
import keyDown from './events/keyDown';
|
||||
import moveListToListItem from './transforms/moveListToListItem';
|
||||
import toggleListType from './events/toggleListType';
|
||||
|
||||
function withLists(editor) {
|
||||
const { normalizeNode } = editor;
|
||||
if (editor.keyDownHandlers === undefined) {
|
||||
editor.keyDownHandlers = [];
|
||||
}
|
||||
editor.keyDownHandlers.push((event, editor) => keyDown(event, editor));
|
||||
|
||||
editor.toggleList = type => toggleListType(editor, type);
|
||||
|
||||
editor.isListItem = () => {
|
||||
const { selection } = editor;
|
||||
if (!selection) return false;
|
||||
|
||||
const [match] = Array.from(
|
||||
Editor.nodes(editor, {
|
||||
at: Editor.unhangRange(editor, selection),
|
||||
match: n =>
|
||||
!Editor.isEditor(n) &&
|
||||
Element.isElement(n) &&
|
||||
Editor.isBlock(editor, n) &&
|
||||
n.type !== 'paragraph' &&
|
||||
!`${n.type}`.startsWith('heading-'),
|
||||
mode: 'lowest',
|
||||
}),
|
||||
);
|
||||
|
||||
return !!match && match[0].type === 'list-item';
|
||||
};
|
||||
|
||||
editor.normalizeNode = entry => {
|
||||
normalizeNode(entry);
|
||||
const [node, path] = entry;
|
||||
|
||||
let previousType = null;
|
||||
if (Element.isElement(node) || Editor.isEditor(node)) {
|
||||
for (const [child, childPath] of Node.children(editor, path)) {
|
||||
if (`${child.type}`.endsWith('-list') && child.type === previousType) {
|
||||
Transforms.mergeNodes(editor, { at: childPath });
|
||||
break;
|
||||
}
|
||||
previousType = child.type;
|
||||
}
|
||||
}
|
||||
|
||||
if (Element.isElement(node) && `${node.type}`.endsWith('-list')) {
|
||||
const previousNode = Editor.previous(editor, { at: path });
|
||||
const [parentNode, parentNodePath] = Editor.parent(editor, path);
|
||||
|
||||
if (!previousNode && parentNode.type === 'list-item') {
|
||||
const previousListItem = Editor.previous(editor, { at: parentNodePath });
|
||||
moveListToListItem(editor, path, previousListItem);
|
||||
Transforms.removeNodes(editor, { at: parentNodePath });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return editor;
|
||||
}
|
||||
|
||||
export default withLists;
|
||||
6
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/matchers/lowestMatchedAncestor.js
generated
vendored
Normal file
6
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/matchers/lowestMatchedAncestor.js
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
import matchedAncestors from './matchedAncestors';
|
||||
|
||||
function lowestMatchedAncestor(editor, format) {
|
||||
return matchedAncestors(editor, format, 'lowest');
|
||||
}
|
||||
export default lowestMatchedAncestor;
|
||||
9
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/matchers/matchLink.js
generated
vendored
Normal file
9
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/matchers/matchLink.js
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
import { Editor, Element } from 'slate';
|
||||
|
||||
function matchLink() {
|
||||
return {
|
||||
match: n => !Editor.isEditor(n) && Element.isElement(n) && n.type === 'link',
|
||||
};
|
||||
}
|
||||
|
||||
export default matchLink;
|
||||
18
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/matchers/matchedAncestors.js
generated
vendored
Normal file
18
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/matchers/matchedAncestors.js
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Editor, Element } from 'slate';
|
||||
|
||||
function matchedAncestors(editor, format, mode) {
|
||||
return {
|
||||
match: n =>
|
||||
(!Editor.isEditor(n) &&
|
||||
Element.isElement(n) &&
|
||||
Editor.isBlock(editor, n) &&
|
||||
(n.type === format ||
|
||||
(format === 'heading' && `${n.type}`.startsWith('heading-')) ||
|
||||
(format === 'paragraph' && `${n.type}`.startsWith('heading-')) ||
|
||||
(format === 'block' && !`${n.type}`.startsWith('heading-') && n.type !== 'paragraph') ||
|
||||
(format === 'list' && `${n.type}`.endsWith('-list')))) ||
|
||||
(format === 'non-default' && n.type !== 'paragraph'),
|
||||
mode,
|
||||
};
|
||||
}
|
||||
export default matchedAncestors;
|
||||
31
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/shortcodes/insertShortcode.js
generated
vendored
Normal file
31
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/shortcodes/insertShortcode.js
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
import { Transforms } from 'slate';
|
||||
|
||||
import isCursorInEmptyParagraph from './locations/isCursorInEmptyParagraph';
|
||||
|
||||
function insertShortcode(editor, pluginConfig) {
|
||||
const defaultValues = pluginConfig.fields
|
||||
.toMap()
|
||||
.mapKeys((_, field) => field.get('name'))
|
||||
.filter(field => field.has('default'))
|
||||
.map(field => field.get('default'));
|
||||
|
||||
const nodeData = {
|
||||
type: 'shortcode',
|
||||
id: pluginConfig.id,
|
||||
data: {
|
||||
shortcode: pluginConfig.id,
|
||||
shortcodeNew: true,
|
||||
shortcodeData: defaultValues.toJS(),
|
||||
},
|
||||
children: [{ text: '' }],
|
||||
};
|
||||
|
||||
if (isCursorInEmptyParagraph(editor)) {
|
||||
Transforms.setNodes(editor, nodeData);
|
||||
return;
|
||||
}
|
||||
|
||||
Transforms.insertNodes(editor, nodeData);
|
||||
}
|
||||
|
||||
export default insertShortcode;
|
||||
17
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/shortcodes/locations/isCursorInEmptyParagraph.js
generated
vendored
Normal file
17
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/shortcodes/locations/isCursorInEmptyParagraph.js
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Editor, Element } from 'slate';
|
||||
|
||||
function isCursorInEmptyParagraph(editor) {
|
||||
const { selection } = editor;
|
||||
if (!selection) return false;
|
||||
|
||||
const [match] = Array.from(
|
||||
Editor.nodes(editor, {
|
||||
match: n => Element.isElement(n) && Editor.isBlock(editor, n) && n.type === 'paragraph',
|
||||
mode: 'lowest',
|
||||
}),
|
||||
);
|
||||
|
||||
return !!match && Editor.isEmpty(editor, match[0]);
|
||||
}
|
||||
|
||||
export default isCursorInEmptyParagraph;
|
||||
26
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/shortcodes/withShortcodes.js
generated
vendored
Normal file
26
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/shortcodes/withShortcodes.js
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
import { Editor, Transforms } from 'slate';
|
||||
|
||||
import defaultEmptyBlock from '../blocks/defaultEmptyBlock';
|
||||
|
||||
function withShortcodes(editor) {
|
||||
const { isVoid, normalizeNode } = editor;
|
||||
|
||||
editor.isVoid = element => {
|
||||
return element.type === 'shortcode' ? true : isVoid(element);
|
||||
};
|
||||
|
||||
// Prevent empty editor after deleting shortcode theat was only child
|
||||
editor.normalizeNode = entry => {
|
||||
const [node] = entry;
|
||||
|
||||
if (Editor.isEditor(node) && node.children.length == 0) {
|
||||
Transforms.insertNodes(editor, defaultEmptyBlock());
|
||||
}
|
||||
|
||||
normalizeNode(entry);
|
||||
};
|
||||
|
||||
return editor;
|
||||
}
|
||||
|
||||
export default withShortcodes;
|
||||
11
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/util.js
generated
vendored
Normal file
11
node_modules/decap-cms-widget-markdown/src/MarkdownControl/plugins/util.js
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import { castArray, isArray } from 'lodash';
|
||||
|
||||
export function assertType(nodes, type) {
|
||||
const nodesArray = castArray(nodes);
|
||||
const validate = isArray(type) ? node => type.includes(node.type) : node => type === node.type;
|
||||
const invalidNode = nodesArray.find(node => !validate(node));
|
||||
if (invalidNode) {
|
||||
throw Error(`Expected node of type "${type}", received "${invalidNode.type}".`);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
357
node_modules/decap-cms-widget-markdown/src/MarkdownControl/renderers.js
generated
vendored
Normal file
357
node_modules/decap-cms-widget-markdown/src/MarkdownControl/renderers.js
generated
vendored
Normal file
@@ -0,0 +1,357 @@
|
||||
/* eslint-disable react/display-name */
|
||||
import React from 'react';
|
||||
import { css } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { colors, lengths } from 'decap-cms-ui-default';
|
||||
import { useSelected } from 'slate-react';
|
||||
|
||||
import VoidBlock from './components/VoidBlock';
|
||||
import Shortcode from './components/Shortcode';
|
||||
|
||||
const bottomMargin = '16px';
|
||||
|
||||
const headerStyles = `
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
`;
|
||||
|
||||
const StyledH1 = styled.h1`
|
||||
${headerStyles};
|
||||
font-size: 32px;
|
||||
margin-top: 16px;
|
||||
`;
|
||||
|
||||
const StyledH2 = styled.h2`
|
||||
${headerStyles};
|
||||
font-size: 24px;
|
||||
margin-top: 12px;
|
||||
`;
|
||||
|
||||
const StyledH3 = styled.h3`
|
||||
${headerStyles};
|
||||
font-size: 20px;
|
||||
`;
|
||||
|
||||
const StyledH4 = styled.h4`
|
||||
${headerStyles};
|
||||
font-size: 18px;
|
||||
margin-top: 8px;
|
||||
`;
|
||||
|
||||
const StyledH5 = styled.h5`
|
||||
${headerStyles};
|
||||
font-size: 16px;
|
||||
margin-top: 8px;
|
||||
`;
|
||||
|
||||
const StyledH6 = StyledH5.withComponent('h6');
|
||||
|
||||
const StyledP = styled.p`
|
||||
margin-bottom: ${bottomMargin};
|
||||
`;
|
||||
|
||||
const StyledBlockQuote = styled.blockquote`
|
||||
padding-left: 16px;
|
||||
border-left: 3px solid ${colors.background};
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
margin-bottom: ${bottomMargin};
|
||||
`;
|
||||
|
||||
const StyledCode = styled.code`
|
||||
background-color: ${colors.background};
|
||||
border-radius: ${lengths.borderRadius};
|
||||
padding: 0 2px;
|
||||
font-size: 85%;
|
||||
`;
|
||||
|
||||
const StyledUl = styled.ul`
|
||||
margin-bottom: ${bottomMargin};
|
||||
padding-left: 30px;
|
||||
`;
|
||||
|
||||
const StyledOl = StyledUl.withComponent('ol');
|
||||
|
||||
const StyledLi = styled.li`
|
||||
& > p:first-of-type {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
& > p:last-of-type {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledA = styled.a`
|
||||
text-decoration: underline;
|
||||
font-size: inherit;
|
||||
`;
|
||||
|
||||
const StyledHr = styled.hr`
|
||||
border: 1px solid;
|
||||
margin-bottom: 16px;
|
||||
`;
|
||||
|
||||
const StyledTable = styled.table`
|
||||
border-collapse: collapse;
|
||||
`;
|
||||
|
||||
const StyledTd = styled.td`
|
||||
border: 2px solid black;
|
||||
padding: 8px;
|
||||
text-align: left;
|
||||
`;
|
||||
|
||||
/**
|
||||
* Slate uses React components to render each type of node that it receives.
|
||||
* This is the closest thing Slate has to a schema definition. The types are set
|
||||
* by us when we manually deserialize from Remark's MDAST to Slate's AST.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Mark Components
|
||||
*/
|
||||
function Bold(props) {
|
||||
return <strong>{props.children}</strong>;
|
||||
}
|
||||
|
||||
function Italic(props) {
|
||||
return <em>{props.children}</em>;
|
||||
}
|
||||
|
||||
function Strikethrough(props) {
|
||||
return <s>{props.children}</s>;
|
||||
}
|
||||
|
||||
function Code(props) {
|
||||
return <StyledCode>{props.children}</StyledCode>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Node Components
|
||||
*/
|
||||
function Paragraph(props) {
|
||||
return <StyledP {...props.attributes}>{props.children}</StyledP>;
|
||||
}
|
||||
|
||||
function ListItem(props) {
|
||||
return <StyledLi {...props.attributes}>{props.children}</StyledLi>;
|
||||
}
|
||||
|
||||
function Quote(props) {
|
||||
return <StyledBlockQuote {...props.attributes}>{props.children}</StyledBlockQuote>;
|
||||
}
|
||||
|
||||
function HeadingOne(props) {
|
||||
return <StyledH1 {...props.attributes}>{props.children}</StyledH1>;
|
||||
}
|
||||
|
||||
function HeadingTwo(props) {
|
||||
return <StyledH2 {...props.attributes}>{props.children}</StyledH2>;
|
||||
}
|
||||
|
||||
function HeadingThree(props) {
|
||||
return <StyledH3 {...props.attributes}>{props.children}</StyledH3>;
|
||||
}
|
||||
|
||||
function HeadingFour(props) {
|
||||
return <StyledH4 {...props.attributes}>{props.children}</StyledH4>;
|
||||
}
|
||||
|
||||
function HeadingFive(props) {
|
||||
return <StyledH5 {...props.attributes}>{props.children}</StyledH5>;
|
||||
}
|
||||
|
||||
function HeadingSix(props) {
|
||||
return <StyledH6 {...props.attributes}>{props.children}</StyledH6>;
|
||||
}
|
||||
|
||||
function Table(props) {
|
||||
return (
|
||||
<StyledTable>
|
||||
<tbody {...props.attributes}>{props.children}</tbody>
|
||||
</StyledTable>
|
||||
);
|
||||
}
|
||||
|
||||
function TableRow(props) {
|
||||
return <tr {...props.attributes}>{props.children}</tr>;
|
||||
}
|
||||
|
||||
function TableCell(props) {
|
||||
return <StyledTd {...props.attributes}>{props.children}</StyledTd>;
|
||||
}
|
||||
|
||||
function ThematicBreak(props) {
|
||||
const isSelected = useSelected();
|
||||
return (
|
||||
<div {...props.attributes}>
|
||||
{props.children}
|
||||
<div contentEditable={false}>
|
||||
<StyledHr
|
||||
{...props.attributes}
|
||||
css={
|
||||
isSelected &&
|
||||
css`
|
||||
box-shadow: 0 0 0 2px ${colors.active};
|
||||
border-radius: 8px;
|
||||
color: ${colors.active};
|
||||
`
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Break(props) {
|
||||
return (
|
||||
<>
|
||||
<br {...props.attributes} />
|
||||
{props.children}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function BulletedList(props) {
|
||||
return <StyledUl {...props.attributes}>{props.children}</StyledUl>;
|
||||
}
|
||||
|
||||
function NumberedList(props) {
|
||||
return (
|
||||
<StyledOl {...props.attributes} start={1}>
|
||||
{props.children}
|
||||
</StyledOl>
|
||||
);
|
||||
}
|
||||
|
||||
function Link(props) {
|
||||
const url = props.url;
|
||||
const title = props.title || url;
|
||||
|
||||
return (
|
||||
<StyledA href={url} title={title} {...props.attributes}>
|
||||
{props.children}
|
||||
</StyledA>
|
||||
);
|
||||
}
|
||||
|
||||
function Image(props) {
|
||||
const { url, title, alt } = props.element.data;
|
||||
const isSelected = useSelected();
|
||||
return (
|
||||
<span {...props.attributes}>
|
||||
{props.children}
|
||||
<img
|
||||
src={url}
|
||||
title={title}
|
||||
alt={alt}
|
||||
{...props.attributes}
|
||||
css={
|
||||
isSelected &&
|
||||
css`
|
||||
box-shadow: 0 0 0 2px ${colors.active};
|
||||
`
|
||||
}
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
export function Leaf({ attributes, children, leaf }) {
|
||||
if (leaf.bold) {
|
||||
children = <Bold>{children}</Bold>;
|
||||
}
|
||||
|
||||
if (leaf.italic) {
|
||||
children = <Italic>{children}</Italic>;
|
||||
}
|
||||
|
||||
if (leaf.delete) {
|
||||
children = <Strikethrough>{children}</Strikethrough>;
|
||||
}
|
||||
|
||||
if (leaf.code) {
|
||||
children = <Code>{children}</Code>;
|
||||
}
|
||||
|
||||
// if (leaf.break) {
|
||||
// children = <Break />;
|
||||
// }
|
||||
|
||||
return <span {...attributes}>{children}</span>;
|
||||
}
|
||||
|
||||
export function renderInline__DEPRECATED() {
|
||||
return props => {
|
||||
switch (props.node.type) {
|
||||
case 'link':
|
||||
return <Link {...props} />;
|
||||
case 'image':
|
||||
return <Image {...props} />;
|
||||
case 'break':
|
||||
return <Break {...props} />;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function Element(props) {
|
||||
const { children, element, classNameWrapper, codeBlockComponent } = props;
|
||||
const style = { textAlign: element.align };
|
||||
|
||||
switch (element.type) {
|
||||
case 'bulleted-list':
|
||||
return <BulletedList>{children}</BulletedList>;
|
||||
case 'quote':
|
||||
return <Quote>{children}</Quote>;
|
||||
case 'heading-one':
|
||||
return <HeadingOne>{children}</HeadingOne>;
|
||||
case 'heading-two':
|
||||
return <HeadingTwo>{children}</HeadingTwo>;
|
||||
case 'heading-three':
|
||||
return <HeadingThree>{children}</HeadingThree>;
|
||||
case 'heading-four':
|
||||
return <HeadingFour>{children}</HeadingFour>;
|
||||
case 'heading-five':
|
||||
return <HeadingFive>{children}</HeadingFive>;
|
||||
case 'heading-six':
|
||||
return <HeadingSix>{children}</HeadingSix>;
|
||||
case 'list-item':
|
||||
return <ListItem>{children}</ListItem>;
|
||||
case 'numbered-list':
|
||||
return <NumberedList>{children}</NumberedList>;
|
||||
case 'table':
|
||||
return <Table {...props} />;
|
||||
case 'table-row':
|
||||
return <TableRow {...props} />;
|
||||
case 'table-cell':
|
||||
return <TableCell {...props} />;
|
||||
case 'thematic-break':
|
||||
return (
|
||||
<VoidBlock {...props}>
|
||||
<ThematicBreak {...props} />
|
||||
</VoidBlock>
|
||||
);
|
||||
case 'link':
|
||||
return <Link {...props} />;
|
||||
case 'image':
|
||||
return <Image {...props} />;
|
||||
case 'break':
|
||||
return <Break {...props} />;
|
||||
case 'shortcode':
|
||||
if (element.id === 'code-block' && codeBlockComponent) {
|
||||
return (
|
||||
<VoidBlock {...props}>
|
||||
<Shortcode classNameWrapper={classNameWrapper} typeOverload="code-block" {...props} />
|
||||
</VoidBlock>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<VoidBlock {...props}>
|
||||
<Shortcode {...props}>{children}</Shortcode>
|
||||
</VoidBlock>
|
||||
);
|
||||
default:
|
||||
return <Paragraph style={style}>{children}</Paragraph>;
|
||||
}
|
||||
}
|
||||
27
node_modules/decap-cms-widget-markdown/src/MarkdownPreview.js
generated
vendored
Normal file
27
node_modules/decap-cms-widget-markdown/src/MarkdownPreview.js
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { WidgetPreviewContainer } from 'decap-cms-ui-default';
|
||||
import DOMPurify from 'dompurify';
|
||||
|
||||
import { markdownToHtml } from './serializers';
|
||||
class MarkdownPreview extends React.Component {
|
||||
static propTypes = {
|
||||
getAsset: PropTypes.func.isRequired,
|
||||
resolveWidget: PropTypes.func.isRequired,
|
||||
value: PropTypes.string,
|
||||
};
|
||||
|
||||
render() {
|
||||
const { value, getAsset, resolveWidget, field, getRemarkPlugins } = this.props;
|
||||
if (value === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const html = markdownToHtml(value, { getAsset, resolveWidget }, getRemarkPlugins?.());
|
||||
const toRender = field?.get('sanitize_preview', false) ? DOMPurify.sanitize(html) : html;
|
||||
|
||||
return <WidgetPreviewContainer dangerouslySetInnerHTML={{ __html: toRender }} />;
|
||||
}
|
||||
}
|
||||
|
||||
export default MarkdownPreview;
|
||||
239
node_modules/decap-cms-widget-markdown/src/__tests__/__snapshots__/renderer.spec.js.snap
generated
vendored
Normal file
239
node_modules/decap-cms-widget-markdown/src/__tests__/__snapshots__/renderer.spec.js.snap
generated
vendored
Normal file
@@ -0,0 +1,239 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Markdown Preview renderer HTML rendering should render HTML 1`] = `
|
||||
.emotion-0 {
|
||||
margin: 15px 2px;
|
||||
}
|
||||
|
||||
<div
|
||||
className="emotion-0 emotion-1"
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "<p>Paragraph with <em>inline</em> element</p>",
|
||||
}
|
||||
}
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`Markdown Preview renderer HTML sanitization should not sanitize HTML 1`] = `
|
||||
.emotion-0 {
|
||||
margin: 15px 2px;
|
||||
}
|
||||
|
||||
<div
|
||||
className="emotion-0 emotion-1"
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "<img src=\\"foobar.png\\" onerror=\\"alert('hello')\\">",
|
||||
}
|
||||
}
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`Markdown Preview renderer HTML sanitization should sanitize HTML 1`] = `
|
||||
.emotion-0 {
|
||||
margin: 15px 2px;
|
||||
}
|
||||
|
||||
<div
|
||||
className="emotion-0 emotion-1"
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "<img src=\\"foobar.png\\">",
|
||||
}
|
||||
}
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`Markdown Preview renderer Markdown rendering Code should render code 1`] = `
|
||||
.emotion-0 {
|
||||
margin: 15px 2px;
|
||||
}
|
||||
|
||||
<div
|
||||
className="emotion-0 emotion-1"
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "<p>Use the <code>printf()</code> function.</p>",
|
||||
}
|
||||
}
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`Markdown Preview renderer Markdown rendering Code should render code 2 1`] = `
|
||||
.emotion-0 {
|
||||
margin: 15px 2px;
|
||||
}
|
||||
|
||||
<div
|
||||
className="emotion-0 emotion-1"
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "<p><code>There is a literal backtick (\`) here.</code></p>",
|
||||
}
|
||||
}
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`Markdown Preview renderer Markdown rendering General should render markdown 1`] = `
|
||||
.emotion-0 {
|
||||
margin: 15px 2px;
|
||||
}
|
||||
|
||||
<div
|
||||
className="emotion-0 emotion-1"
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "<h1>H1</h1>
|
||||
<p>Text with <strong>bold</strong> & <em>em</em> elements</p>
|
||||
<h2>H2</h2>
|
||||
<ul>
|
||||
<li>ul item 1</li>
|
||||
<li>ul item 2</li>
|
||||
</ul>
|
||||
<h3>H3</h3>
|
||||
<ol>
|
||||
<li>ol item 1</li>
|
||||
<li>ol item 2</li>
|
||||
<li>ol item 3</li>
|
||||
</ol>
|
||||
<h4>H4</h4>
|
||||
<p><a href=\\"http://google.com\\">link title</a></p>
|
||||
<h5>H5</h5>
|
||||
<p><img src=\\"https://pbs.twimg.com/profile_images/678903331176214528/TQTdqGwD.jpg\\" alt=\\"alt text\\" /></p>
|
||||
<h6>H6</h6>
|
||||
<p><img src=\\"https://pbs.twimg.com/profile_images/678903331176214528/TQTdqGwD.jpg\\" /></p>",
|
||||
}
|
||||
}
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`Markdown Preview renderer Markdown rendering HTML should render HTML as is when using Markdown 1`] = `
|
||||
.emotion-0 {
|
||||
margin: 15px 2px;
|
||||
}
|
||||
|
||||
<div
|
||||
className="emotion-0 emotion-1"
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "<h1>Title</h1>
|
||||
<form action=\\"test\\">
|
||||
<label for=\\"input\\">
|
||||
<input type=\\"checkbox\\" checked=\\"checked\\" id=\\"input\\"/> My label
|
||||
</label>
|
||||
<dl class=\\"test-class another-class\\" style=\\"width: 100%\\">
|
||||
<dt data-attr=\\"test\\">Test HTML content</dt>
|
||||
<dt>Testing HTML in Markdown</dt>
|
||||
</dl>
|
||||
</form>
|
||||
<h1 style=\\"display: block; border: 10px solid #f00; width: 100%\\">Test</h1>",
|
||||
}
|
||||
}
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`Markdown Preview renderer Markdown rendering Headings should render Heading 1 1`] = `
|
||||
.emotion-0 {
|
||||
margin: 15px 2px;
|
||||
}
|
||||
|
||||
<div
|
||||
className="emotion-0 emotion-1"
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "<h1>Title</h1>",
|
||||
}
|
||||
}
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`Markdown Preview renderer Markdown rendering Headings should render Heading 2 1`] = `
|
||||
.emotion-0 {
|
||||
margin: 15px 2px;
|
||||
}
|
||||
|
||||
<div
|
||||
className="emotion-0 emotion-1"
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "<h2>Title</h2>",
|
||||
}
|
||||
}
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`Markdown Preview renderer Markdown rendering Headings should render Heading 3 1`] = `
|
||||
.emotion-0 {
|
||||
margin: 15px 2px;
|
||||
}
|
||||
|
||||
<div
|
||||
className="emotion-0 emotion-1"
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "<h3>Title</h3>",
|
||||
}
|
||||
}
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`Markdown Preview renderer Markdown rendering Headings should render Heading 4 1`] = `
|
||||
.emotion-0 {
|
||||
margin: 15px 2px;
|
||||
}
|
||||
|
||||
<div
|
||||
className="emotion-0 emotion-1"
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "<h4>Title</h4>",
|
||||
}
|
||||
}
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`Markdown Preview renderer Markdown rendering Headings should render Heading 5 1`] = `
|
||||
.emotion-0 {
|
||||
margin: 15px 2px;
|
||||
}
|
||||
|
||||
<div
|
||||
className="emotion-0 emotion-1"
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "<h5>Title</h5>",
|
||||
}
|
||||
}
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`Markdown Preview renderer Markdown rendering Headings should render Heading 6 1`] = `
|
||||
.emotion-0 {
|
||||
margin: 15px 2px;
|
||||
}
|
||||
|
||||
<div
|
||||
className="emotion-0 emotion-1"
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "<h6>Title</h6>",
|
||||
}
|
||||
}
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`Markdown Preview renderer Markdown rendering Links should render links 1`] = `
|
||||
.emotion-0 {
|
||||
margin: 15px 2px;
|
||||
}
|
||||
|
||||
<div
|
||||
className="emotion-0 emotion-1"
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "<p>I get 10 times more traffic from <a href=\\"http://google.com/\\" title=\\"Google\\">Google</a> than from <a href=\\"http://search.yahoo.com/\\" title=\\"Yahoo Search\\">Yahoo</a> or <a href=\\"http://search.msn.com/\\" title=\\"MSN Search\\">MSN</a>.</p>",
|
||||
}
|
||||
}
|
||||
/>
|
||||
`;
|
||||
261
node_modules/decap-cms-widget-markdown/src/__tests__/renderer.spec.js
generated
vendored
Normal file
261
node_modules/decap-cms-widget-markdown/src/__tests__/renderer.spec.js
generated
vendored
Normal file
@@ -0,0 +1,261 @@
|
||||
import React from 'react';
|
||||
import { create, act } from 'react-test-renderer';
|
||||
import { padStart } from 'lodash';
|
||||
import { Map } from 'immutable';
|
||||
|
||||
import MarkdownPreview from '../MarkdownPreview';
|
||||
import { markdownToHtml } from '../serializers';
|
||||
|
||||
describe('Markdown Preview renderer', () => {
|
||||
describe('Markdown rendering', () => {
|
||||
describe('General', () => {
|
||||
it('should render markdown', async () => {
|
||||
const value = `
|
||||
# H1
|
||||
|
||||
Text with **bold** & _em_ elements
|
||||
|
||||
## H2
|
||||
|
||||
* ul item 1
|
||||
* ul item 2
|
||||
|
||||
### H3
|
||||
|
||||
1. ol item 1
|
||||
1. ol item 2
|
||||
1. ol item 3
|
||||
|
||||
#### H4
|
||||
|
||||
[link title](http://google.com)
|
||||
|
||||
##### H5
|
||||
|
||||

|
||||
|
||||
###### H6
|
||||
|
||||

|
||||
`;
|
||||
const html = await markdownToHtml(value);
|
||||
|
||||
let root;
|
||||
await act(async () => {
|
||||
root = create(
|
||||
<MarkdownPreview value={html} getAsset={jest.fn()} resolveWidget={jest.fn()} />,
|
||||
);
|
||||
});
|
||||
|
||||
expect(root.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Headings', () => {
|
||||
for (const heading of [...Array(6).keys()]) {
|
||||
it(`should render Heading ${heading + 1}`, async () => {
|
||||
const value = padStart(' Title', heading + 7, '#');
|
||||
const html = await markdownToHtml(value);
|
||||
|
||||
let root;
|
||||
await act(async () => {
|
||||
root = create(
|
||||
<MarkdownPreview value={html} getAsset={jest.fn()} resolveWidget={jest.fn()} />,
|
||||
);
|
||||
});
|
||||
|
||||
expect(root.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe('Lists', () => {
|
||||
it('should render lists', async () => {
|
||||
const value = `
|
||||
1. ol item 1
|
||||
1. ol item 2
|
||||
* Sublist 1
|
||||
* Sublist 2
|
||||
* Sublist 3
|
||||
1. Sub-Sublist 1
|
||||
1. Sub-Sublist 2
|
||||
1. Sub-Sublist 3
|
||||
1. ol item 3
|
||||
`;
|
||||
const html = await markdownToHtml(value);
|
||||
|
||||
let root;
|
||||
await act(async () => {
|
||||
root = create(
|
||||
<MarkdownPreview value={html} getAsset={jest.fn()} resolveWidget={jest.fn()} />,
|
||||
);
|
||||
});
|
||||
|
||||
expect(root.toJSON()).toMatchInlineSnapshot(`
|
||||
.emotion-0 {
|
||||
margin: 15px 2px;
|
||||
}
|
||||
|
||||
<div
|
||||
className="emotion-0 emotion-1"
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "<ol>
|
||||
<li>ol item 1</li>
|
||||
<li>ol item 2<ul>
|
||||
<li>Sublist 1</li>
|
||||
<li>Sublist 2</li>
|
||||
<li>Sublist 3<ol>
|
||||
<li>Sub-Sublist 1</li>
|
||||
<li>Sub-Sublist 2</li>
|
||||
<li>Sub-Sublist 3</li>
|
||||
</ol></li>
|
||||
</ul></li>
|
||||
<li>ol item 3</li>
|
||||
</ol>",
|
||||
}
|
||||
}
|
||||
/>
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Links', () => {
|
||||
it('should render links', async () => {
|
||||
const value = `
|
||||
I get 10 times more traffic from [Google] than from [Yahoo] or [MSN].
|
||||
|
||||
[Google]: http://google.com/ "Google"
|
||||
[Yahoo]: http://search.yahoo.com/ "Yahoo Search"
|
||||
[MSN]: http://search.msn.com/ "MSN Search"
|
||||
`;
|
||||
const html = await markdownToHtml(value);
|
||||
|
||||
let root;
|
||||
await act(async () => {
|
||||
root = create(
|
||||
<MarkdownPreview value={html} getAsset={jest.fn()} resolveWidget={jest.fn()} />,
|
||||
);
|
||||
});
|
||||
|
||||
expect(root.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Code', () => {
|
||||
it('should render code', async () => {
|
||||
const value = 'Use the `printf()` function.';
|
||||
const html = await markdownToHtml(value);
|
||||
|
||||
let root;
|
||||
await act(async () => {
|
||||
root = create(
|
||||
<MarkdownPreview value={html} getAsset={jest.fn()} resolveWidget={jest.fn()} />,
|
||||
);
|
||||
});
|
||||
|
||||
expect(root.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render code 2', async () => {
|
||||
const value = '``There is a literal backtick (`) here.``';
|
||||
const html = await markdownToHtml(value);
|
||||
|
||||
let root;
|
||||
await act(async () => {
|
||||
root = create(
|
||||
<MarkdownPreview value={html} getAsset={jest.fn()} resolveWidget={jest.fn()} />,
|
||||
);
|
||||
});
|
||||
|
||||
expect(root.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('HTML', () => {
|
||||
it('should render HTML as is when using Markdown', async () => {
|
||||
const value = `
|
||||
# Title
|
||||
|
||||
<form action="test">
|
||||
<label for="input">
|
||||
<input type="checkbox" checked="checked" id="input"/> My label
|
||||
</label>
|
||||
<dl class="test-class another-class" style="width: 100%">
|
||||
<dt data-attr="test">Test HTML content</dt>
|
||||
<dt>Testing HTML in Markdown</dt>
|
||||
</dl>
|
||||
</form>
|
||||
|
||||
<h1 style="display: block; border: 10px solid #f00; width: 100%">Test</h1>
|
||||
`;
|
||||
const html = await markdownToHtml(value);
|
||||
|
||||
let root;
|
||||
await act(async () => {
|
||||
root = create(
|
||||
<MarkdownPreview value={html} getAsset={jest.fn()} resolveWidget={jest.fn()} />,
|
||||
);
|
||||
});
|
||||
|
||||
expect(root.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('HTML rendering', () => {
|
||||
it('should render HTML', async () => {
|
||||
const value = '<p>Paragraph with <em>inline</em> element</p>';
|
||||
const html = await markdownToHtml(value);
|
||||
|
||||
let root;
|
||||
await act(async () => {
|
||||
root = create(
|
||||
<MarkdownPreview value={html} getAsset={jest.fn()} resolveWidget={jest.fn()} />,
|
||||
);
|
||||
});
|
||||
|
||||
expect(root.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('HTML sanitization', () => {
|
||||
it('should sanitize HTML', async () => {
|
||||
const value = `<img src="foobar.png" onerror="alert('hello')">`;
|
||||
const field = Map({ sanitize_preview: true });
|
||||
|
||||
let root;
|
||||
await act(async () => {
|
||||
root = create(
|
||||
<MarkdownPreview
|
||||
value={value}
|
||||
getAsset={jest.fn()}
|
||||
resolveWidget={jest.fn()}
|
||||
field={field}
|
||||
/>,
|
||||
);
|
||||
});
|
||||
|
||||
expect(root.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should not sanitize HTML', async () => {
|
||||
const value = `<img src="foobar.png" onerror="alert('hello')">`;
|
||||
const field = Map({ sanitize_preview: false });
|
||||
|
||||
let root;
|
||||
await act(async () => {
|
||||
root = create(
|
||||
<MarkdownPreview
|
||||
value={value}
|
||||
getAsset={jest.fn()}
|
||||
resolveWidget={jest.fn()}
|
||||
field={field}
|
||||
/>,
|
||||
);
|
||||
});
|
||||
|
||||
expect(root.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
16
node_modules/decap-cms-widget-markdown/src/index.js
generated
vendored
Normal file
16
node_modules/decap-cms-widget-markdown/src/index.js
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
import controlComponent from './MarkdownControl';
|
||||
import previewComponent from './MarkdownPreview';
|
||||
import schema from './schema';
|
||||
|
||||
function Widget(opts = {}) {
|
||||
return {
|
||||
name: 'markdown',
|
||||
controlComponent,
|
||||
previewComponent,
|
||||
schema,
|
||||
...opts,
|
||||
};
|
||||
}
|
||||
|
||||
export const DecapCmsWidgetMarkdown = { Widget, controlComponent, previewComponent };
|
||||
export default DecapCmsWidgetMarkdown;
|
||||
137
node_modules/decap-cms-widget-markdown/src/regexHelper.js
generated
vendored
Normal file
137
node_modules/decap-cms-widget-markdown/src/regexHelper.js
generated
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
import { last } from 'lodash';
|
||||
|
||||
/**
|
||||
* Joins an array of regular expressions into a single expression, without
|
||||
* altering the received expressions.
|
||||
*/
|
||||
export function joinPatternSegments(patterns) {
|
||||
return patterns.map(p => p.source).join('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines an array of regular expressions into a single expression, wrapping
|
||||
* each in a non-capturing group and interposing alternation characters (|) so
|
||||
* that each expression is executed separately.
|
||||
*/
|
||||
export function combinePatterns(patterns) {
|
||||
return patterns.map(p => `(?:${p.source})`).join('|');
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify substrings within a string if they match a (global) pattern. Can be
|
||||
* inverted to only modify non-matches.
|
||||
*
|
||||
* params:
|
||||
* matchPattern - regexp - a regular expression to check for matches
|
||||
* replaceFn - function - a replacement function that receives a matched
|
||||
* substring and returns a replacement substring
|
||||
* text - string - the string to process
|
||||
* invertMatchPattern - boolean - if true, non-matching substrings are modified
|
||||
* instead of matching substrings
|
||||
*/
|
||||
export function replaceWhen(matchPattern, replaceFn, text, invertMatchPattern) {
|
||||
/**
|
||||
* Splits the string into an array of objects with the following shape:
|
||||
*
|
||||
* {
|
||||
* index: number - the index of the substring within the string
|
||||
* text: string - the substring
|
||||
* match: boolean - true if the substring matched `matchPattern`
|
||||
* }
|
||||
*
|
||||
* Loops through matches via recursion (`RegExp.exec` tracks the loop
|
||||
* internally).
|
||||
*/
|
||||
function split(exp, text, acc) {
|
||||
/**
|
||||
* Get the next match starting from the end of the last match or start of
|
||||
* string.
|
||||
*/
|
||||
const match = exp.exec(text);
|
||||
const lastEntry = last(acc);
|
||||
|
||||
/**
|
||||
* `match` will be null if there are no matches.
|
||||
*/
|
||||
if (!match) return acc;
|
||||
|
||||
/**
|
||||
* If the match is at the beginning of the input string, normalize to a data
|
||||
* object with the `match` flag set to `true`, and add to the accumulator.
|
||||
*/
|
||||
if (match.index === 0) {
|
||||
addSubstring(acc, 0, match[0], true);
|
||||
} else if (!lastEntry) {
|
||||
/**
|
||||
* If there are no entries in the accumulator, convert the substring before
|
||||
* the match to a data object (without the `match` flag set to true) and
|
||||
* push to the accumulator, followed by a data object for the matching
|
||||
* substring.
|
||||
*/
|
||||
addSubstring(acc, 0, match.input.slice(0, match.index));
|
||||
addSubstring(acc, match.index, match[0], true);
|
||||
} else if (match.index === lastEntry.index + lastEntry.text.length) {
|
||||
/**
|
||||
* If the last entry in the accumulator immediately preceded the current
|
||||
* matched substring in the original string, just add the data object for
|
||||
* the matching substring to the accumulator.
|
||||
*/
|
||||
addSubstring(acc, match.index, match[0], true);
|
||||
} else {
|
||||
/**
|
||||
* Convert the substring before the match to a data object (without the
|
||||
* `match` flag set to true), followed by a data object for the matching
|
||||
* substring.
|
||||
*/
|
||||
const nextIndex = lastEntry.index + lastEntry.text.length;
|
||||
const nextText = match.input.slice(nextIndex, match.index);
|
||||
addSubstring(acc, nextIndex, nextText);
|
||||
addSubstring(acc, match.index, match[0], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Continue executing the expression.
|
||||
*/
|
||||
return split(exp, text, acc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory for converting substrings to data objects and adding to an output
|
||||
* array.
|
||||
*/
|
||||
function addSubstring(arr, index, text, match = false) {
|
||||
arr.push({ index, text, match });
|
||||
}
|
||||
|
||||
/**
|
||||
* Split the input string to an array of data objects, each representing a
|
||||
* matching or non-matching string.
|
||||
*/
|
||||
const acc = split(matchPattern, text, []);
|
||||
|
||||
/**
|
||||
* Process the trailing substring after the final match, if one exists.
|
||||
*/
|
||||
const lastEntry = last(acc);
|
||||
if (!lastEntry) return replaceFn(text);
|
||||
|
||||
const nextIndex = lastEntry.index + lastEntry.text.length;
|
||||
if (text.length > nextIndex) {
|
||||
acc.push({ index: nextIndex, text: text.slice(nextIndex) });
|
||||
}
|
||||
|
||||
/**
|
||||
* Map the data objects in the accumulator to their string values, modifying
|
||||
* matched strings with the replacement function. Modifies non-matches if
|
||||
* `invertMatchPattern` is truthy.
|
||||
*/
|
||||
const replacedText = acc.map(entry => {
|
||||
const isMatch = invertMatchPattern ? !entry.match : entry.match;
|
||||
return isMatch ? replaceFn(entry.text) : entry.text;
|
||||
});
|
||||
|
||||
/**
|
||||
* Return the joined string.
|
||||
*/
|
||||
return replacedText.join('');
|
||||
}
|
||||
35
node_modules/decap-cms-widget-markdown/src/schema.js
generated
vendored
Normal file
35
node_modules/decap-cms-widget-markdown/src/schema.js
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
export default {
|
||||
properties: {
|
||||
minimal: { type: 'boolean' },
|
||||
buttons: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string',
|
||||
enum: [
|
||||
'bold',
|
||||
'italic',
|
||||
'code',
|
||||
'link',
|
||||
'heading-one',
|
||||
'heading-two',
|
||||
'heading-three',
|
||||
'heading-four',
|
||||
'heading-five',
|
||||
'heading-six',
|
||||
'quote',
|
||||
'bulleted-list',
|
||||
'numbered-list',
|
||||
],
|
||||
},
|
||||
},
|
||||
editor_components: { type: 'array', items: { type: 'string' } },
|
||||
modes: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string',
|
||||
enum: ['raw', 'rich_text'],
|
||||
},
|
||||
minItems: 1,
|
||||
},
|
||||
},
|
||||
};
|
||||
625
node_modules/decap-cms-widget-markdown/src/serializers/__tests__/__fixtures__/commonmarkExpected.json
generated
vendored
Normal file
625
node_modules/decap-cms-widget-markdown/src/serializers/__tests__/__fixtures__/commonmarkExpected.json
generated
vendored
Normal file
@@ -0,0 +1,625 @@
|
||||
{
|
||||
"\tfoo\tbaz\t\tbim\n": "NOT_TO_EQUAL",
|
||||
" \tfoo\tbaz\t\tbim\n": "NOT_TO_EQUAL",
|
||||
" a\ta\n ὐ\ta\n": "NOT_TO_EQUAL",
|
||||
" - foo\n\n\tbar\n": "NOT_TO_EQUAL",
|
||||
"- foo\n\n\t\tbar\n": "NOT_TO_EQUAL",
|
||||
">\t\tfoo\n": "NOT_TO_EQUAL",
|
||||
"-\t\tfoo\n": "NOT_TO_EQUAL",
|
||||
" foo\n\tbar\n": "TO_EQUAL",
|
||||
" - foo\n - bar\n\t - baz\n": "NOT_TO_EQUAL",
|
||||
"#\tFoo\n": "TO_EQUAL",
|
||||
"*\t*\t*\t\n": "TO_ERROR",
|
||||
"- `one\n- two`\n": "TO_EQUAL",
|
||||
"***\n---\n___\n": "NOT_TO_EQUAL",
|
||||
"+++\n": "TO_EQUAL",
|
||||
"===\n": "TO_EQUAL",
|
||||
"--\n**\n__\n": "TO_EQUAL",
|
||||
" ***\n ***\n ***\n": "NOT_TO_EQUAL",
|
||||
" ***\n": "TO_EQUAL",
|
||||
"Foo\n ***\n": "TO_EQUAL",
|
||||
"_____________________________________\n": "NOT_TO_EQUAL",
|
||||
" - - -\n": "NOT_TO_EQUAL",
|
||||
" ** * ** * ** * **\n": "NOT_TO_EQUAL",
|
||||
"- - - -\n": "NOT_TO_EQUAL",
|
||||
"- - - - \n": "NOT_TO_EQUAL",
|
||||
"_ _ _ _ a\n\na------\n\n---a---\n": "TO_EQUAL",
|
||||
" *-*\n": "NOT_TO_EQUAL",
|
||||
"- foo\n***\n- bar\n": "NOT_TO_EQUAL",
|
||||
"Foo\n***\nbar\n": "NOT_TO_EQUAL",
|
||||
"Foo\n---\nbar\n": "TO_EQUAL",
|
||||
"* Foo\n* * *\n* Bar\n": "NOT_TO_EQUAL",
|
||||
"- Foo\n- * * *\n": "NOT_TO_EQUAL",
|
||||
"# foo\n## foo\n### foo\n#### foo\n##### foo\n###### foo\n": "TO_EQUAL",
|
||||
"####### foo\n": "TO_EQUAL",
|
||||
"#5 bolt\n\n#hashtag\n": "TO_EQUAL",
|
||||
"\\## foo\n": "TO_EQUAL",
|
||||
"# foo *bar* \\*baz\\*\n": "NOT_TO_EQUAL",
|
||||
"# foo \n": "TO_EQUAL",
|
||||
" ### foo\n ## foo\n # foo\n": "TO_EQUAL",
|
||||
" # foo\n": "TO_EQUAL",
|
||||
"foo\n # bar\n": "TO_EQUAL",
|
||||
"## foo ##\n ### bar ###\n": "TO_EQUAL",
|
||||
"# foo ##################################\n##### foo ##\n": "TO_EQUAL",
|
||||
"### foo ### \n": "TO_EQUAL",
|
||||
"### foo ### b\n": "TO_EQUAL",
|
||||
"# foo#\n": "NOT_TO_EQUAL",
|
||||
"### foo \\###\n## foo #\\##\n# foo \\#\n": "NOT_TO_EQUAL",
|
||||
"****\n## foo\n****\n": "NOT_TO_EQUAL",
|
||||
"Foo bar\n# baz\nBar foo\n": "TO_EQUAL",
|
||||
"## \n#\n### ###\n": "TO_ERROR",
|
||||
"Foo *bar*\n=========\n\nFoo *bar*\n---------\n": "TO_EQUAL",
|
||||
"Foo *bar\nbaz*\n====\n": "NOT_TO_EQUAL",
|
||||
"Foo\n-------------------------\n\nFoo\n=\n": "TO_EQUAL",
|
||||
" Foo\n---\n\n Foo\n-----\n\n Foo\n ===\n": "NOT_TO_EQUAL",
|
||||
" Foo\n ---\n\n Foo\n---\n": "NOT_TO_EQUAL",
|
||||
"Foo\n ---- \n": "NOT_TO_EQUAL",
|
||||
"Foo\n ---\n": "TO_EQUAL",
|
||||
"Foo\n= =\n\nFoo\n--- -\n": "NOT_TO_EQUAL",
|
||||
"Foo \n-----\n": "TO_EQUAL",
|
||||
"Foo\\\n----\n": "TO_EQUAL",
|
||||
"`Foo\n----\n`\n\n<a title=\"a lot\n---\nof dashes\"/>\n": "NOT_TO_EQUAL",
|
||||
"> Foo\n---\n": "NOT_TO_EQUAL",
|
||||
"> foo\nbar\n===\n": "NOT_TO_EQUAL",
|
||||
"- Foo\n---\n": "NOT_TO_EQUAL",
|
||||
"Foo\nBar\n---\n": "NOT_TO_EQUAL",
|
||||
"---\nFoo\n---\nBar\n---\nBaz\n": "NOT_TO_EQUAL",
|
||||
"\n====\n": "TO_EQUAL",
|
||||
"---\n---\n": "TO_ERROR",
|
||||
"- foo\n-----\n": "NOT_TO_EQUAL",
|
||||
" foo\n---\n": "NOT_TO_EQUAL",
|
||||
"> foo\n-----\n": "NOT_TO_EQUAL",
|
||||
"\\> foo\n------\n": "NOT_TO_EQUAL",
|
||||
"Foo\n\nbar\n---\nbaz\n": "TO_EQUAL",
|
||||
"Foo\nbar\n\n---\n\nbaz\n": "NOT_TO_EQUAL",
|
||||
"Foo\nbar\n* * *\nbaz\n": "NOT_TO_EQUAL",
|
||||
"Foo\nbar\n\\---\nbaz\n": "NOT_TO_EQUAL",
|
||||
" a simple\n indented code block\n": "TO_EQUAL",
|
||||
" - foo\n\n bar\n": "NOT_TO_EQUAL",
|
||||
"1. foo\n\n - bar\n": "TO_EQUAL",
|
||||
" <a/>\n *hi*\n\n - one\n": "NOT_TO_EQUAL",
|
||||
" chunk1\n\n chunk2\n \n \n \n chunk3\n": "TO_EQUAL",
|
||||
" chunk1\n \n chunk2\n": "TO_EQUAL",
|
||||
"Foo\n bar\n\n": "TO_EQUAL",
|
||||
" foo\nbar\n": "TO_EQUAL",
|
||||
"# Heading\n foo\nHeading\n------\n foo\n----\n": "NOT_TO_EQUAL",
|
||||
" foo\n bar\n": "TO_EQUAL",
|
||||
"\n \n foo\n \n\n": "TO_EQUAL",
|
||||
" foo \n": "TO_EQUAL",
|
||||
"```\n<\n >\n```\n": "NOT_TO_EQUAL",
|
||||
"~~~\n<\n >\n~~~\n": "NOT_TO_EQUAL",
|
||||
"``\nfoo\n``\n": "TO_EQUAL",
|
||||
"```\naaa\n~~~\n```\n": "NOT_TO_EQUAL",
|
||||
"~~~\naaa\n```\n~~~\n": "NOT_TO_EQUAL",
|
||||
"````\naaa\n```\n``````\n": "NOT_TO_EQUAL",
|
||||
"~~~~\naaa\n~~~\n~~~~\n": "NOT_TO_EQUAL",
|
||||
"```\n": "TO_EQUAL",
|
||||
"`````\n\n```\naaa\n": "NOT_TO_EQUAL",
|
||||
"> ```\n> aaa\n\nbbb\n": "TO_EQUAL",
|
||||
"```\n\n \n```\n": "NOT_TO_EQUAL",
|
||||
"```\n```\n": "TO_EQUAL",
|
||||
" ```\n aaa\naaa\n```\n": "TO_EQUAL",
|
||||
" ```\naaa\n aaa\naaa\n ```\n": "TO_EQUAL",
|
||||
" ```\n aaa\n aaa\n aaa\n ```\n": "TO_EQUAL",
|
||||
" ```\n aaa\n ```\n": "NOT_TO_EQUAL",
|
||||
"```\naaa\n ```\n": "TO_EQUAL",
|
||||
" ```\naaa\n ```\n": "TO_EQUAL",
|
||||
"```\naaa\n ```\n": "NOT_TO_EQUAL",
|
||||
"``` ```\naaa\n": "TO_EQUAL",
|
||||
"~~~~~~\naaa\n~~~ ~~\n": "NOT_TO_EQUAL",
|
||||
"foo\n```\nbar\n```\nbaz\n": "TO_EQUAL",
|
||||
"foo\n---\n~~~\nbar\n~~~\n# baz\n": "TO_EQUAL",
|
||||
"```ruby\ndef foo(x)\n return 3\nend\n```\n": "TO_EQUAL",
|
||||
"~~~~ ruby startline=3 $%@#$\ndef foo(x)\n return 3\nend\n~~~~~~~\n": "TO_EQUAL",
|
||||
"````;\n````\n": "TO_EQUAL",
|
||||
"``` aa ```\nfoo\n": "TO_EQUAL",
|
||||
"```\n``` aaa\n```\n": "NOT_TO_EQUAL",
|
||||
"<table><tr><td>\n<pre>\n**Hello**,\n\n_world_.\n</pre>\n</td></tr></table>\n": "NOT_TO_EQUAL",
|
||||
"<table>\n <tr>\n <td>\n hi\n </td>\n </tr>\n</table>\n\nokay.\n": "TO_EQUAL",
|
||||
" <div>\n *hello*\n <foo><a>\n": "NOT_TO_EQUAL",
|
||||
"</div>\n*foo*\n": "NOT_TO_EQUAL",
|
||||
"<DIV CLASS=\"foo\">\n\n*Markdown*\n\n</DIV>\n": "TO_EQUAL",
|
||||
"<div id=\"foo\"\n class=\"bar\">\n</div>\n": "TO_EQUAL",
|
||||
"<div id=\"foo\" class=\"bar\n baz\">\n</div>\n": "TO_EQUAL",
|
||||
"<div>\n*foo*\n\n*bar*\n": "NOT_TO_EQUAL",
|
||||
"<div id=\"foo\"\n*hi*\n": "NOT_TO_EQUAL",
|
||||
"<div class\nfoo\n": "TO_EQUAL",
|
||||
"<div *???-&&&-<---\n*foo*\n": "NOT_TO_EQUAL",
|
||||
"<div><a href=\"bar\">*foo*</a></div>\n": "NOT_TO_EQUAL",
|
||||
"<table><tr><td>\nfoo\n</td></tr></table>\n": "TO_EQUAL",
|
||||
"<div></div>\n``` c\nint x = 33;\n```\n": "NOT_TO_EQUAL",
|
||||
"<a href=\"foo\">\n*bar*\n</a>\n": "NOT_TO_EQUAL",
|
||||
"<Warning>\n*bar*\n</Warning>\n": "NOT_TO_EQUAL",
|
||||
"<i class=\"foo\">\n*bar*\n</i>\n": "NOT_TO_EQUAL",
|
||||
"</ins>\n*bar*\n": "NOT_TO_EQUAL",
|
||||
"<del>\n*foo*\n</del>\n": "NOT_TO_EQUAL",
|
||||
"<del>\n\n*foo*\n\n</del>\n": "TO_EQUAL",
|
||||
"<del>*foo*</del>\n": "TO_EQUAL",
|
||||
"<pre language=\"haskell\"><code>\nimport Text.HTML.TagSoup\n\nmain :: IO ()\nmain = print $ parseTags tags\n</code></pre>\nokay\n": "TO_EQUAL",
|
||||
"<script type=\"text/javascript\">\n// JavaScript example\n\ndocument.getElementById(\"demo\").innerHTML = \"Hello JavaScript!\";\n</script>\nokay\n": "TO_EQUAL",
|
||||
"<style\n type=\"text/css\">\nh1 {color:red;}\n\np {color:blue;}\n</style>\nokay\n": "TO_EQUAL",
|
||||
"<style\n type=\"text/css\">\n\nfoo\n": "TO_EQUAL",
|
||||
"> <div>\n> foo\n\nbar\n": "TO_EQUAL",
|
||||
"- <div>\n- foo\n": "TO_EQUAL",
|
||||
"<style>p{color:red;}</style>\n*foo*\n": "TO_EQUAL",
|
||||
"<!-- foo -->*bar*\n*baz*\n": "NOT_TO_EQUAL",
|
||||
"<script>\nfoo\n</script>1. *bar*\n": "NOT_TO_EQUAL",
|
||||
"<!-- Foo\n\nbar\n baz -->\nokay\n": "TO_EQUAL",
|
||||
"<?php\n\n echo '>';\n\n?>\nokay\n": "TO_EQUAL",
|
||||
"<!DOCTYPE html>\n": "TO_EQUAL",
|
||||
"<![CDATA[\nfunction matchwo(a,b)\n{\n if (a < b && a < 0) then {\n return 1;\n\n } else {\n\n return 0;\n }\n}\n]]>\nokay\n": "NOT_TO_EQUAL",
|
||||
" <!-- foo -->\n\n <!-- foo -->\n": "NOT_TO_EQUAL",
|
||||
" <div>\n\n <div>\n": "NOT_TO_EQUAL",
|
||||
"Foo\n<div>\nbar\n</div>\n": "TO_EQUAL",
|
||||
"<div>\nbar\n</div>\n*foo*\n": "NOT_TO_EQUAL",
|
||||
"Foo\n<a href=\"bar\">\nbaz\n": "TO_EQUAL",
|
||||
"<div>\n\n*Emphasized* text.\n\n</div>\n": "TO_EQUAL",
|
||||
"<div>\n*Emphasized* text.\n</div>\n": "NOT_TO_EQUAL",
|
||||
"<table>\n\n<tr>\n\n<td>\nHi\n</td>\n\n</tr>\n\n</table>\n": "TO_EQUAL",
|
||||
"<table>\n\n <tr>\n\n <td>\n Hi\n </td>\n\n </tr>\n\n</table>\n": "NOT_TO_EQUAL",
|
||||
"[foo]: /url \"title\"\n\n[foo]\n": "TO_EQUAL",
|
||||
" [foo]: \n /url \n 'the title' \n\n[foo]\n": "TO_EQUAL",
|
||||
"[Foo*bar\\]]:my_(url) 'title (with parens)'\n\n[Foo*bar\\]]\n": "NOT_TO_EQUAL",
|
||||
"[Foo bar]:\n<my%20url>\n'title'\n\n[Foo bar]\n": "TO_EQUAL",
|
||||
"[foo]: /url '\ntitle\nline1\nline2\n'\n\n[foo]\n": "NOT_TO_EQUAL",
|
||||
"[foo]: /url 'title\n\nwith blank line'\n\n[foo]\n": "TO_EQUAL",
|
||||
"[foo]:\n/url\n\n[foo]\n": "TO_EQUAL",
|
||||
"[foo]:\n\n[foo]\n": "TO_EQUAL",
|
||||
"[foo]: /url\\bar\\*baz \"foo\\\"bar\\baz\"\n\n[foo]\n": "NOT_TO_EQUAL",
|
||||
"[foo]\n\n[foo]: url\n": "TO_EQUAL",
|
||||
"[foo]\n\n[foo]: first\n[foo]: second\n": "NOT_TO_EQUAL",
|
||||
"[FOO]: /url\n\n[Foo]\n": "TO_EQUAL",
|
||||
"[ΑΓΩ]: /φου\n\n[αγω]\n": "TO_EQUAL",
|
||||
"[foo]: /url\n": "TO_ERROR",
|
||||
"[\nfoo\n]: /url\nbar\n": "TO_EQUAL",
|
||||
"[foo]: /url \"title\" ok\n": "NOT_TO_EQUAL",
|
||||
"[foo]: /url\n\"title\" ok\n": "NOT_TO_EQUAL",
|
||||
" [foo]: /url \"title\"\n\n[foo]\n": "NOT_TO_EQUAL",
|
||||
"```\n[foo]: /url\n```\n\n[foo]\n": "TO_EQUAL",
|
||||
"Foo\n[bar]: /baz\n\n[bar]\n": "TO_EQUAL",
|
||||
"# [Foo]\n[foo]: /url\n> bar\n": "TO_EQUAL",
|
||||
"[foo]: /foo-url \"foo\"\n[bar]: /bar-url\n \"bar\"\n[baz]: /baz-url\n\n[foo],\n[bar],\n[baz]\n": "TO_EQUAL",
|
||||
"[foo]\n\n> [foo]: /url\n": "NOT_TO_EQUAL",
|
||||
"aaa\n\nbbb\n": "TO_EQUAL",
|
||||
"aaa\nbbb\n\nccc\nddd\n": "TO_EQUAL",
|
||||
"aaa\n\n\nbbb\n": "TO_EQUAL",
|
||||
" aaa\n bbb\n": "NOT_TO_EQUAL",
|
||||
"aaa\n bbb\n ccc\n": "TO_EQUAL",
|
||||
" aaa\nbbb\n": "NOT_TO_EQUAL",
|
||||
" aaa\nbbb\n": "TO_EQUAL",
|
||||
"aaa \nbbb \n": "NOT_TO_EQUAL",
|
||||
" \n\naaa\n \n\n# aaa\n\n \n": "TO_EQUAL",
|
||||
"> # Foo\n> bar\n> baz\n": "TO_EQUAL",
|
||||
"># Foo\n>bar\n> baz\n": "TO_EQUAL",
|
||||
" > # Foo\n > bar\n > baz\n": "TO_EQUAL",
|
||||
" > # Foo\n > bar\n > baz\n": "NOT_TO_EQUAL",
|
||||
"> # Foo\n> bar\nbaz\n": "TO_EQUAL",
|
||||
"> bar\nbaz\n> foo\n": "TO_EQUAL",
|
||||
"> foo\n---\n": "NOT_TO_EQUAL",
|
||||
"> - foo\n- bar\n": "TO_EQUAL",
|
||||
"> foo\n bar\n": "TO_EQUAL",
|
||||
"> ```\nfoo\n```\n": "NOT_TO_EQUAL",
|
||||
"> foo\n - bar\n": "NOT_TO_EQUAL",
|
||||
">\n": "TO_ERROR",
|
||||
">\n> \n> \n": "TO_ERROR",
|
||||
">\n> foo\n> \n": "TO_EQUAL",
|
||||
"> foo\n\n> bar\n": "NOT_TO_EQUAL",
|
||||
"> foo\n> bar\n": "TO_EQUAL",
|
||||
"> foo\n>\n> bar\n": "TO_EQUAL",
|
||||
"foo\n> bar\n": "TO_EQUAL",
|
||||
"> aaa\n***\n> bbb\n": "NOT_TO_EQUAL",
|
||||
"> bar\nbaz\n": "TO_EQUAL",
|
||||
"> bar\n\nbaz\n": "TO_EQUAL",
|
||||
"> bar\n>\nbaz\n": "NOT_TO_EQUAL",
|
||||
"> > > foo\nbar\n": "TO_EQUAL",
|
||||
">>> foo\n> bar\n>>baz\n": "TO_EQUAL",
|
||||
"> code\n\n> not code\n": "NOT_TO_EQUAL",
|
||||
"A paragraph\nwith two lines.\n\n indented code\n\n> A block quote.\n": "TO_EQUAL",
|
||||
"1. A paragraph\n with two lines.\n\n indented code\n\n > A block quote.\n": "TO_EQUAL",
|
||||
"- one\n\n two\n": "NOT_TO_EQUAL",
|
||||
"- one\n\n two\n": "NOT_TO_EQUAL",
|
||||
" - one\n\n two\n": "NOT_TO_EQUAL",
|
||||
" - one\n\n two\n": "NOT_TO_EQUAL",
|
||||
" > > 1. one\n>>\n>> two\n": "NOT_TO_EQUAL",
|
||||
">>- one\n>>\n > > two\n": "TO_EQUAL",
|
||||
"-one\n\n2.two\n": "TO_EQUAL",
|
||||
"- foo\n\n\n bar\n": "TO_EQUAL",
|
||||
"1. foo\n\n ```\n bar\n ```\n\n baz\n\n > bam\n": "TO_EQUAL",
|
||||
"- Foo\n\n bar\n\n\n baz\n": "NOT_TO_EQUAL",
|
||||
"123456789. ok\n": "TO_EQUAL",
|
||||
"1234567890. not ok\n": "NOT_TO_EQUAL",
|
||||
"0. ok\n": "NOT_TO_EQUAL",
|
||||
"003. ok\n": "TO_EQUAL",
|
||||
"-1. not ok\n": "TO_EQUAL",
|
||||
"- foo\n\n bar\n": "TO_EQUAL",
|
||||
" 10. foo\n\n bar\n": "TO_EQUAL",
|
||||
" indented code\n\nparagraph\n\n more code\n": "TO_EQUAL",
|
||||
"1. indented code\n\n paragraph\n\n more code\n": "TO_EQUAL",
|
||||
"1. indented code\n\n paragraph\n\n more code\n": "TO_EQUAL",
|
||||
" foo\n\nbar\n": "NOT_TO_EQUAL",
|
||||
"- foo\n\n bar\n": "NOT_TO_EQUAL",
|
||||
"- foo\n\n bar\n": "NOT_TO_EQUAL",
|
||||
"-\n foo\n-\n ```\n bar\n ```\n-\n baz\n": "NOT_TO_EQUAL",
|
||||
"- \n foo\n": "TO_ERROR",
|
||||
"-\n\n foo\n": "NOT_TO_EQUAL",
|
||||
"- foo\n-\n- bar\n": "TO_ERROR",
|
||||
"- foo\n- \n- bar\n": "TO_ERROR",
|
||||
"1. foo\n2.\n3. bar\n": "TO_ERROR",
|
||||
"*\n": "NOT_TO_EQUAL",
|
||||
"foo\n*\n\nfoo\n1.\n": "TO_EQUAL",
|
||||
" 1. A paragraph\n with two lines.\n\n indented code\n\n > A block quote.\n": "TO_EQUAL",
|
||||
" 1. A paragraph\n with two lines.\n\n indented code\n\n > A block quote.\n": "TO_EQUAL",
|
||||
" 1. A paragraph\n with two lines.\n\n indented code\n\n > A block quote.\n": "TO_EQUAL",
|
||||
" 1. A paragraph\n with two lines.\n\n indented code\n\n > A block quote.\n": "NOT_TO_EQUAL",
|
||||
" 1. A paragraph\nwith two lines.\n\n indented code\n\n > A block quote.\n": "NOT_TO_EQUAL",
|
||||
" 1. A paragraph\n with two lines.\n": "TO_EQUAL",
|
||||
"> 1. > Blockquote\ncontinued here.\n": "TO_EQUAL",
|
||||
"> 1. > Blockquote\n> continued here.\n": "TO_EQUAL",
|
||||
"- foo\n - bar\n - baz\n - boo\n": "NOT_TO_EQUAL",
|
||||
"- foo\n - bar\n - baz\n - boo\n": "TO_EQUAL",
|
||||
"10) foo\n - bar\n": "NOT_TO_EQUAL",
|
||||
"10) foo\n - bar\n": "TO_EQUAL",
|
||||
"- - foo\n": "TO_EQUAL",
|
||||
"1. - 2. foo\n": "TO_EQUAL",
|
||||
"- # Foo\n- Bar\n ---\n baz\n": "NOT_TO_EQUAL",
|
||||
"- foo\n- bar\n+ baz\n": "TO_EQUAL",
|
||||
"1. foo\n2. bar\n3) baz\n": "TO_EQUAL",
|
||||
"Foo\n- bar\n- baz\n": "TO_EQUAL",
|
||||
"The number of windows in my house is\n14. The number of doors is 6.\n": "NOT_TO_EQUAL",
|
||||
"The number of windows in my house is\n1. The number of doors is 6.\n": "TO_EQUAL",
|
||||
"- foo\n\n- bar\n\n\n- baz\n": "NOT_TO_EQUAL",
|
||||
"- foo\n - bar\n - baz\n\n\n bim\n": "NOT_TO_EQUAL",
|
||||
"- foo\n- bar\n\n<!-- -->\n\n- baz\n- bim\n": "TO_EQUAL",
|
||||
"- foo\n\n notcode\n\n- foo\n\n<!-- -->\n\n code\n": "NOT_TO_EQUAL",
|
||||
"- a\n - b\n - c\n - d\n - e\n - f\n - g\n - h\n- i\n": "NOT_TO_EQUAL",
|
||||
"1. a\n\n 2. b\n\n 3. c\n": "NOT_TO_EQUAL",
|
||||
"- a\n- b\n\n- c\n": "NOT_TO_EQUAL",
|
||||
"* a\n*\n\n* c\n": "TO_ERROR",
|
||||
"- a\n- b\n\n c\n- d\n": "NOT_TO_EQUAL",
|
||||
"- a\n- b\n\n [ref]: /url\n- d\n": "NOT_TO_EQUAL",
|
||||
"- a\n- ```\n b\n\n\n ```\n- c\n": "NOT_TO_EQUAL",
|
||||
"- a\n - b\n\n c\n- d\n": "NOT_TO_EQUAL",
|
||||
"* a\n > b\n >\n* c\n": "NOT_TO_EQUAL",
|
||||
"- a\n > b\n ```\n c\n ```\n- d\n": "NOT_TO_EQUAL",
|
||||
"- a\n": "TO_EQUAL",
|
||||
"- a\n - b\n": "NOT_TO_EQUAL",
|
||||
"1. ```\n foo\n ```\n\n bar\n": "TO_EQUAL",
|
||||
"* foo\n * bar\n\n baz\n": "NOT_TO_EQUAL",
|
||||
"- a\n - b\n - c\n\n- d\n - e\n - f\n": "TO_EQUAL",
|
||||
"`hi`lo`\n": "TO_EQUAL",
|
||||
"\\!\\\"\\#\\$\\%\\&\\'\\(\\)\\*\\+\\,\\-\\.\\/\\:\\;\\<\\=\\>\\?\\@\\[\\\\\\]\\^\\_\\`\\{\\|\\}\\~\n": "NOT_TO_EQUAL",
|
||||
"\\\t\\A\\a\\ \\3\\φ\\«\n": "TO_EQUAL",
|
||||
"\\*not emphasized*\n\\<br/> not a tag\n\\[not a link](/foo)\n\\`not code`\n1\\. not a list\n\\* not a list\n\\# not a heading\n\\[foo]: /url \"not a reference\"\n": "NOT_TO_EQUAL",
|
||||
"\\\\*emphasis*\n": "NOT_TO_EQUAL",
|
||||
"foo\\\nbar\n": "NOT_TO_EQUAL",
|
||||
"`` \\[\\` ``\n": "TO_EQUAL",
|
||||
" \\[\\]\n": "TO_EQUAL",
|
||||
"~~~\n\\[\\]\n~~~\n": "TO_EQUAL",
|
||||
"<http://example.com?find=\\*>\n": "NOT_TO_EQUAL",
|
||||
"<a href=\"/bar\\/)\">\n": "TO_EQUAL",
|
||||
"[foo](/bar\\* \"ti\\*tle\")\n": "TO_EQUAL",
|
||||
"[foo]\n\n[foo]: /bar\\* \"ti\\*tle\"\n": "TO_EQUAL",
|
||||
"``` foo\\+bar\nfoo\n```\n": "TO_EQUAL",
|
||||
" & © Æ Ď\n¾ ℋ ⅆ\n∲ ≧̸\n": "NOT_TO_EQUAL",
|
||||
"# Ӓ Ϡ � �\n": "NOT_TO_EQUAL",
|
||||
"" ആ ಫ\n": "NOT_TO_EQUAL",
|
||||
"  &x; &#; &#x;\n&ThisIsNotDefined; &hi?;\n": "NOT_TO_EQUAL",
|
||||
"©\n": "NOT_TO_EQUAL",
|
||||
"&MadeUpEntity;\n": "NOT_TO_EQUAL",
|
||||
"<a href=\"öö.html\">\n": "TO_EQUAL",
|
||||
"[foo](/föö \"föö\")\n": "TO_EQUAL",
|
||||
"[foo]\n\n[foo]: /föö \"föö\"\n": "TO_EQUAL",
|
||||
"``` föö\nfoo\n```\n": "TO_EQUAL",
|
||||
"`föö`\n": "NOT_TO_EQUAL",
|
||||
" föfö\n": "NOT_TO_EQUAL",
|
||||
"`foo`\n": "TO_EQUAL",
|
||||
"`` foo ` bar ``\n": "TO_EQUAL",
|
||||
"` `` `\n": "TO_EQUAL",
|
||||
"`foo bar\n baz`\n": "TO_EQUAL",
|
||||
"`a b`\n": "NOT_TO_EQUAL",
|
||||
"`foo `` bar`\n": "TO_EQUAL",
|
||||
"`foo\\`bar`\n": "TO_EQUAL",
|
||||
"*foo`*`\n": "NOT_TO_EQUAL",
|
||||
"[not a `link](/foo`)\n": "NOT_TO_EQUAL",
|
||||
"`<a href=\"`\">`\n": "NOT_TO_EQUAL",
|
||||
"<a href=\"`\">`\n": "TO_EQUAL",
|
||||
"`<http://foo.bar.`baz>`\n": "NOT_TO_EQUAL",
|
||||
"<http://foo.bar.`baz>`\n": "TO_EQUAL",
|
||||
"```foo``\n": "NOT_TO_EQUAL",
|
||||
"`foo\n": "TO_EQUAL",
|
||||
"`foo``bar``\n": "NOT_TO_EQUAL",
|
||||
"*foo bar*\n": "TO_EQUAL",
|
||||
"a * foo bar*\n": "NOT_TO_EQUAL",
|
||||
"a*\"foo\"*\n": "NOT_TO_EQUAL",
|
||||
"* a *\n": "NOT_TO_EQUAL",
|
||||
"foo*bar*\n": "TO_EQUAL",
|
||||
"5*6*78\n": "NOT_TO_EQUAL",
|
||||
"_foo bar_\n": "TO_EQUAL",
|
||||
"_ foo bar_\n": "NOT_TO_EQUAL",
|
||||
"a_\"foo\"_\n": "NOT_TO_EQUAL",
|
||||
"foo_bar_\n": "NOT_TO_EQUAL",
|
||||
"5_6_78\n": "TO_EQUAL",
|
||||
"пристаням_стремятся_\n": "NOT_TO_EQUAL",
|
||||
"aa_\"bb\"_cc\n": "NOT_TO_EQUAL",
|
||||
"foo-_(bar)_\n": "TO_EQUAL",
|
||||
"_foo*\n": "TO_EQUAL",
|
||||
"*foo bar *\n": "NOT_TO_EQUAL",
|
||||
"*foo bar\n*\n": "NOT_TO_EQUAL",
|
||||
"*(*foo)\n": "NOT_TO_EQUAL",
|
||||
"*(*foo*)*\n": "NOT_TO_EQUAL",
|
||||
"*foo*bar\n": "NOT_TO_EQUAL",
|
||||
"_foo bar _\n": "NOT_TO_EQUAL",
|
||||
"_(_foo)\n": "TO_EQUAL",
|
||||
"_(_foo_)_\n": "NOT_TO_EQUAL",
|
||||
"_foo_bar\n": "TO_EQUAL",
|
||||
"_пристаням_стремятся\n": "NOT_TO_EQUAL",
|
||||
"_foo_bar_baz_\n": "TO_EQUAL",
|
||||
"_(bar)_.\n": "TO_EQUAL",
|
||||
"**foo bar**\n": "TO_EQUAL",
|
||||
"** foo bar**\n": "NOT_TO_EQUAL",
|
||||
"a**\"foo\"**\n": "NOT_TO_EQUAL",
|
||||
"foo**bar**\n": "TO_EQUAL",
|
||||
"__foo bar__\n": "TO_EQUAL",
|
||||
"__ foo bar__\n": "NOT_TO_EQUAL",
|
||||
"__\nfoo bar__\n": "NOT_TO_EQUAL",
|
||||
"a__\"foo\"__\n": "NOT_TO_EQUAL",
|
||||
"foo__bar__\n": "NOT_TO_EQUAL",
|
||||
"5__6__78\n": "NOT_TO_EQUAL",
|
||||
"пристаням__стремятся__\n": "NOT_TO_EQUAL",
|
||||
"__foo, __bar__, baz__\n": "NOT_TO_EQUAL",
|
||||
"foo-__(bar)__\n": "TO_EQUAL",
|
||||
"**foo bar **\n": "NOT_TO_EQUAL",
|
||||
"**(**foo)\n": "NOT_TO_EQUAL",
|
||||
"*(**foo**)*\n": "TO_EQUAL",
|
||||
"**Gomphocarpus (*Gomphocarpus physocarpus*, syn.\n*Asclepias physocarpa*)**\n": "TO_EQUAL",
|
||||
"**foo \"*bar*\" foo**\n": "NOT_TO_EQUAL",
|
||||
"**foo**bar\n": "TO_EQUAL",
|
||||
"__foo bar __\n": "NOT_TO_EQUAL",
|
||||
"__(__foo)\n": "NOT_TO_EQUAL",
|
||||
"_(__foo__)_\n": "TO_EQUAL",
|
||||
"__foo__bar\n": "NOT_TO_EQUAL",
|
||||
"__пристаням__стремятся\n": "NOT_TO_EQUAL",
|
||||
"__foo__bar__baz__\n": "NOT_TO_EQUAL",
|
||||
"__(bar)__.\n": "TO_EQUAL",
|
||||
"*foo [bar](/url)*\n": "TO_EQUAL",
|
||||
"*foo\nbar*\n": "TO_EQUAL",
|
||||
"_foo __bar__ baz_\n": "TO_EQUAL",
|
||||
"_foo _bar_ baz_\n": "NOT_TO_EQUAL",
|
||||
"__foo_ bar_\n": "NOT_TO_EQUAL",
|
||||
"*foo *bar**\n": "NOT_TO_EQUAL",
|
||||
"*foo **bar** baz*\n": "TO_EQUAL",
|
||||
"*foo**bar**baz*\n": "TO_EQUAL",
|
||||
"***foo** bar*\n": "NOT_TO_EQUAL",
|
||||
"*foo **bar***\n": "NOT_TO_EQUAL",
|
||||
"*foo**bar***\n": "NOT_TO_EQUAL",
|
||||
"*foo **bar *baz* bim** bop*\n": "NOT_TO_EQUAL",
|
||||
"*foo [*bar*](/url)*\n": "NOT_TO_EQUAL",
|
||||
"** is not an empty emphasis\n": "TO_EQUAL",
|
||||
"**** is not an empty strong emphasis\n": "TO_EQUAL",
|
||||
"**foo [bar](/url)**\n": "TO_EQUAL",
|
||||
"**foo\nbar**\n": "TO_EQUAL",
|
||||
"__foo _bar_ baz__\n": "TO_EQUAL",
|
||||
"__foo __bar__ baz__\n": "NOT_TO_EQUAL",
|
||||
"____foo__ bar__\n": "NOT_TO_EQUAL",
|
||||
"**foo **bar****\n": "NOT_TO_EQUAL",
|
||||
"**foo *bar* baz**\n": "TO_EQUAL",
|
||||
"**foo*bar*baz**\n": "NOT_TO_EQUAL",
|
||||
"***foo* bar**\n": "TO_EQUAL",
|
||||
"**foo *bar***\n": "TO_EQUAL",
|
||||
"**foo *bar **baz**\nbim* bop**\n": "NOT_TO_EQUAL",
|
||||
"**foo [*bar*](/url)**\n": "TO_EQUAL",
|
||||
"__ is not an empty emphasis\n": "TO_EQUAL",
|
||||
"____ is not an empty strong emphasis\n": "TO_EQUAL",
|
||||
"foo ***\n": "TO_EQUAL",
|
||||
"foo *\\**\n": "NOT_TO_EQUAL",
|
||||
"foo *_*\n": "NOT_TO_EQUAL",
|
||||
"foo *****\n": "NOT_TO_EQUAL",
|
||||
"foo **\\***\n": "TO_EQUAL",
|
||||
"foo **_**\n": "TO_EQUAL",
|
||||
"**foo*\n": "TO_EQUAL",
|
||||
"*foo**\n": "NOT_TO_EQUAL",
|
||||
"***foo**\n": "NOT_TO_EQUAL",
|
||||
"****foo*\n": "NOT_TO_EQUAL",
|
||||
"**foo***\n": "NOT_TO_EQUAL",
|
||||
"*foo****\n": "NOT_TO_EQUAL",
|
||||
"foo ___\n": "TO_EQUAL",
|
||||
"foo _\\__\n": "NOT_TO_EQUAL",
|
||||
"foo _*_\n": "TO_EQUAL",
|
||||
"foo _____\n": "NOT_TO_EQUAL",
|
||||
"foo __\\___\n": "TO_EQUAL",
|
||||
"foo __*__\n": "TO_EQUAL",
|
||||
"__foo_\n": "TO_EQUAL",
|
||||
"_foo__\n": "NOT_TO_EQUAL",
|
||||
"___foo__\n": "NOT_TO_EQUAL",
|
||||
"____foo_\n": "NOT_TO_EQUAL",
|
||||
"__foo___\n": "NOT_TO_EQUAL",
|
||||
"_foo____\n": "NOT_TO_EQUAL",
|
||||
"**foo**\n": "TO_EQUAL",
|
||||
"*_foo_*\n": "NOT_TO_EQUAL",
|
||||
"__foo__\n": "TO_EQUAL",
|
||||
"_*foo*_\n": "NOT_TO_EQUAL",
|
||||
"****foo****\n": "NOT_TO_EQUAL",
|
||||
"____foo____\n": "NOT_TO_EQUAL",
|
||||
"******foo******\n": "NOT_TO_EQUAL",
|
||||
"***foo***\n": "TO_EQUAL",
|
||||
"_____foo_____\n": "NOT_TO_EQUAL",
|
||||
"*foo _bar* baz_\n": "TO_EQUAL",
|
||||
"*foo __bar *baz bim__ bam*\n": "NOT_TO_EQUAL",
|
||||
"**foo **bar baz**\n": "NOT_TO_EQUAL",
|
||||
"*foo *bar baz*\n": "NOT_TO_EQUAL",
|
||||
"*[bar*](/url)\n": "NOT_TO_EQUAL",
|
||||
"_foo [bar_](/url)\n": "NOT_TO_EQUAL",
|
||||
"*<img src=\"foo\" title=\"*\"/>\n": "NOT_TO_EQUAL",
|
||||
"**<a href=\"**\">\n": "NOT_TO_EQUAL",
|
||||
"__<a href=\"__\">\n": "NOT_TO_EQUAL",
|
||||
"*a `*`*\n": "NOT_TO_EQUAL",
|
||||
"_a `_`_\n": "NOT_TO_EQUAL",
|
||||
"**a<http://foo.bar/?q=**>\n": "NOT_TO_EQUAL",
|
||||
"__a<http://foo.bar/?q=__>\n": "NOT_TO_EQUAL",
|
||||
"[link](/uri \"title\")\n": "TO_EQUAL",
|
||||
"[link](/uri)\n": "TO_EQUAL",
|
||||
"[link]()\n": "TO_EQUAL",
|
||||
"[link](<>)\n": "TO_EQUAL",
|
||||
"[link](/my uri)\n": "TO_EQUAL",
|
||||
"[link](</my uri>)\n": "NOT_TO_EQUAL",
|
||||
"[link](foo\nbar)\n": "TO_EQUAL",
|
||||
"[link](<foo\nbar>)\n": "TO_EQUAL",
|
||||
"[link](\\(foo\\))\n": "TO_EQUAL",
|
||||
"[link](foo(and(bar)))\n": "TO_EQUAL",
|
||||
"[link](foo\\(and\\(bar\\))\n": "TO_EQUAL",
|
||||
"[link](<foo(and(bar)>)\n": "TO_EQUAL",
|
||||
"[link](foo\\)\\:)\n": "TO_EQUAL",
|
||||
"[link](#fragment)\n\n[link](http://example.com#fragment)\n\n[link](http://example.com?foo=3#frag)\n": "TO_EQUAL",
|
||||
"[link](foo\\bar)\n": "TO_EQUAL",
|
||||
"[link](foo%20bä)\n": "TO_EQUAL",
|
||||
"[link](\"title\")\n": "TO_EQUAL",
|
||||
"[link](/url \"title\")\n[link](/url 'title')\n[link](/url (title))\n": "TO_EQUAL",
|
||||
"[link](/url \"title \\\""\")\n": "NOT_TO_EQUAL",
|
||||
"[link](/url \"title\")\n": "NOT_TO_EQUAL",
|
||||
"[link](/url \"title \"and\" title\")\n": "NOT_TO_EQUAL",
|
||||
"[link](/url 'title \"and\" title')\n": "NOT_TO_EQUAL",
|
||||
"[link]( /uri\n \"title\" )\n": "TO_EQUAL",
|
||||
"[link] (/uri)\n": "NOT_TO_EQUAL",
|
||||
"[link [foo [bar]]](/uri)\n": "NOT_TO_EQUAL",
|
||||
"[link] bar](/uri)\n": "TO_EQUAL",
|
||||
"[link [bar](/uri)\n": "TO_EQUAL",
|
||||
"[link \\[bar](/uri)\n": "NOT_TO_EQUAL",
|
||||
"[link *foo **bar** `#`*](/uri)\n": "TO_EQUAL",
|
||||
"[](/uri)\n": "NOT_TO_EQUAL",
|
||||
"[foo [bar](/uri)](/uri)\n": "NOT_TO_EQUAL",
|
||||
"[foo *[bar [baz](/uri)](/uri)*](/uri)\n": "NOT_TO_EQUAL",
|
||||
"](uri2)](uri3)\n": "NOT_TO_EQUAL",
|
||||
"*[foo*](/uri)\n": "NOT_TO_EQUAL",
|
||||
"[foo *bar](baz*)\n": "TO_EQUAL",
|
||||
"*foo [bar* baz]\n": "TO_EQUAL",
|
||||
"[foo <bar attr=\"](baz)\">\n": "NOT_TO_EQUAL",
|
||||
"[foo`](/uri)`\n": "NOT_TO_EQUAL",
|
||||
"[foo<http://example.com/?search=](uri)>\n": "NOT_TO_EQUAL",
|
||||
"[foo][bar]\n\n[bar]: /url \"title\"\n": "TO_EQUAL",
|
||||
"[link [foo [bar]]][ref]\n\n[ref]: /uri\n": "NOT_TO_EQUAL",
|
||||
"[link \\[bar][ref]\n\n[ref]: /uri\n": "NOT_TO_EQUAL",
|
||||
"[link *foo **bar** `#`*][ref]\n\n[ref]: /uri\n": "TO_EQUAL",
|
||||
"[][ref]\n\n[ref]: /uri\n": "NOT_TO_EQUAL",
|
||||
"[foo [bar](/uri)][ref]\n\n[ref]: /uri\n": "NOT_TO_EQUAL",
|
||||
"[foo *bar [baz][ref]*][ref]\n\n[ref]: /uri\n": "NOT_TO_EQUAL",
|
||||
"*[foo*][ref]\n\n[ref]: /uri\n": "NOT_TO_EQUAL",
|
||||
"[foo *bar][ref]\n\n[ref]: /uri\n": "TO_EQUAL",
|
||||
"[foo <bar attr=\"][ref]\">\n\n[ref]: /uri\n": "NOT_TO_EQUAL",
|
||||
"[foo`][ref]`\n\n[ref]: /uri\n": "NOT_TO_EQUAL",
|
||||
"[foo<http://example.com/?search=][ref]>\n\n[ref]: /uri\n": "NOT_TO_EQUAL",
|
||||
"[foo][BaR]\n\n[bar]: /url \"title\"\n": "TO_EQUAL",
|
||||
"[Толпой][Толпой] is a Russian word.\n\n[ТОЛПОЙ]: /url\n": "TO_EQUAL",
|
||||
"[Foo\n bar]: /url\n\n[Baz][Foo bar]\n": "TO_EQUAL",
|
||||
"[foo] [bar]\n\n[bar]: /url \"title\"\n": "NOT_TO_EQUAL",
|
||||
"[foo]\n[bar]\n\n[bar]: /url \"title\"\n": "NOT_TO_EQUAL",
|
||||
"[foo]: /url1\n\n[foo]: /url2\n\n[bar][foo]\n": "NOT_TO_EQUAL",
|
||||
"[bar][foo\\!]\n\n[foo!]: /url\n": "NOT_TO_EQUAL",
|
||||
"[foo][ref[]\n\n[ref[]: /uri\n": "NOT_TO_EQUAL",
|
||||
"[foo][ref[bar]]\n\n[ref[bar]]: /uri\n": "NOT_TO_EQUAL",
|
||||
"[[[foo]]]\n\n[[[foo]]]: /url\n": "TO_EQUAL",
|
||||
"[foo][ref\\[]\n\n[ref\\[]: /uri\n": "TO_EQUAL",
|
||||
"[bar\\\\]: /uri\n\n[bar\\\\]\n": "NOT_TO_EQUAL",
|
||||
"[]\n\n[]: /uri\n": "TO_EQUAL",
|
||||
"[\n ]\n\n[\n ]: /uri\n": "NOT_TO_EQUAL",
|
||||
"[foo][]\n\n[foo]: /url \"title\"\n": "TO_EQUAL",
|
||||
"[*foo* bar][]\n\n[*foo* bar]: /url \"title\"\n": "TO_EQUAL",
|
||||
"[Foo][]\n\n[foo]: /url \"title\"\n": "TO_EQUAL",
|
||||
"[foo] \n[]\n\n[foo]: /url \"title\"\n": "NOT_TO_EQUAL",
|
||||
"[foo]\n\n[foo]: /url \"title\"\n": "TO_EQUAL",
|
||||
"[*foo* bar]\n\n[*foo* bar]: /url \"title\"\n": "TO_EQUAL",
|
||||
"[[*foo* bar]]\n\n[*foo* bar]: /url \"title\"\n": "TO_EQUAL",
|
||||
"[[bar [foo]\n\n[foo]: /url\n": "TO_EQUAL",
|
||||
"[Foo]\n\n[foo]: /url \"title\"\n": "TO_EQUAL",
|
||||
"[foo] bar\n\n[foo]: /url\n": "TO_EQUAL",
|
||||
"\\[foo]\n\n[foo]: /url \"title\"\n": "TO_EQUAL",
|
||||
"[foo*]: /url\n\n*[foo*]\n": "NOT_TO_EQUAL",
|
||||
"[foo][bar]\n\n[foo]: /url1\n[bar]: /url2\n": "TO_EQUAL",
|
||||
"[foo][]\n\n[foo]: /url1\n": "TO_EQUAL",
|
||||
"[foo]()\n\n[foo]: /url1\n": "TO_EQUAL",
|
||||
"[foo](not a link)\n\n[foo]: /url1\n": "TO_EQUAL",
|
||||
"[foo][bar][baz]\n\n[baz]: /url\n": "NOT_TO_EQUAL",
|
||||
"[foo][bar][baz]\n\n[baz]: /url1\n[bar]: /url2\n": "TO_EQUAL",
|
||||
"[foo][bar][baz]\n\n[baz]: /url1\n[foo]: /url2\n": "NOT_TO_EQUAL",
|
||||
"\n": "NOT_TO_EQUAL",
|
||||
"![foo *bar*]\n\n[foo *bar*]: train.jpg \"train & tracks\"\n": "NOT_TO_EQUAL",
|
||||
"](/url2)\n": "NOT_TO_EQUAL",
|
||||
"](/url2)\n": "NOT_TO_EQUAL",
|
||||
"![foo *bar*][]\n\n[foo *bar*]: train.jpg \"train & tracks\"\n": "NOT_TO_EQUAL",
|
||||
"![foo *bar*][foobar]\n\n[FOOBAR]: train.jpg \"train & tracks\"\n": "NOT_TO_EQUAL",
|
||||
"\n": "NOT_TO_EQUAL",
|
||||
"My \n": "NOT_TO_EQUAL",
|
||||
"\n": "NOT_TO_EQUAL",
|
||||
"\n": "NOT_TO_EQUAL",
|
||||
"![foo][bar]\n\n[bar]: /url\n": "NOT_TO_EQUAL",
|
||||
"![foo][bar]\n\n[BAR]: /url\n": "NOT_TO_EQUAL",
|
||||
"![foo][]\n\n[foo]: /url \"title\"\n": "NOT_TO_EQUAL",
|
||||
"![*foo* bar][]\n\n[*foo* bar]: /url \"title\"\n": "NOT_TO_EQUAL",
|
||||
"![Foo][]\n\n[foo]: /url \"title\"\n": "NOT_TO_EQUAL",
|
||||
"![foo] \n[]\n\n[foo]: /url \"title\"\n": "NOT_TO_EQUAL",
|
||||
"![foo]\n\n[foo]: /url \"title\"\n": "NOT_TO_EQUAL",
|
||||
"![*foo* bar]\n\n[*foo* bar]: /url \"title\"\n": "NOT_TO_EQUAL",
|
||||
"![[foo]]\n\n[[foo]]: /url \"title\"\n": "NOT_TO_EQUAL",
|
||||
"![Foo]\n\n[foo]: /url \"title\"\n": "NOT_TO_EQUAL",
|
||||
"!\\[foo]\n\n[foo]: /url \"title\"\n": "TO_EQUAL",
|
||||
"\\![foo]\n\n[foo]: /url \"title\"\n": "NOT_TO_EQUAL",
|
||||
"<http://foo.bar.baz>\n": "TO_EQUAL",
|
||||
"<http://foo.bar.baz/test?q=hello&id=22&boolean>\n": "NOT_TO_EQUAL",
|
||||
"<irc://foo.bar:2233/baz>\n": "TO_EQUAL",
|
||||
"<MAILTO:FOO@BAR.BAZ>\n": "NOT_TO_EQUAL",
|
||||
"<a+b+c:d>\n": "NOT_TO_EQUAL",
|
||||
"<made-up-scheme://foo,bar>\n": "TO_EQUAL",
|
||||
"<http://../>\n": "TO_EQUAL",
|
||||
"<localhost:5001/foo>\n": "NOT_TO_EQUAL",
|
||||
"<http://foo.bar/baz bim>\n": "NOT_TO_EQUAL",
|
||||
"<http://example.com/\\[\\>\n": "NOT_TO_EQUAL",
|
||||
"<foo@bar.example.com>\n": "TO_EQUAL",
|
||||
"<foo+special@Bar.baz-bar0.com>\n": "TO_EQUAL",
|
||||
"<foo\\+@bar.example.com>\n": "NOT_TO_EQUAL",
|
||||
"<>\n": "NOT_TO_EQUAL",
|
||||
"< http://foo.bar >\n": "NOT_TO_EQUAL",
|
||||
"<m:abc>\n": "NOT_TO_EQUAL",
|
||||
"<foo.bar.baz>\n": "NOT_TO_EQUAL",
|
||||
"http://example.com\n": "TO_EQUAL",
|
||||
"foo@bar.example.com\n": "TO_EQUAL",
|
||||
"<a><bab><c2c>\n": "TO_EQUAL",
|
||||
"<a/><b2/>\n": "TO_EQUAL",
|
||||
"<a /><b2\ndata=\"foo\" >\n": "TO_EQUAL",
|
||||
"<a foo=\"bar\" bam = 'baz <em>\"</em>'\n_boolean zoop:33=zoop:33 />\n": "TO_EQUAL",
|
||||
"Foo <responsive-image src=\"foo.jpg\" />\n": "TO_EQUAL",
|
||||
"<33> <__>\n": "NOT_TO_EQUAL",
|
||||
"<a h*#ref=\"hi\">\n": "NOT_TO_EQUAL",
|
||||
"<a href=\"hi'> <a href=hi'>\n": "NOT_TO_EQUAL",
|
||||
"< a><\nfoo><bar/ >\n": "NOT_TO_EQUAL",
|
||||
"<a href='bar'title=title>\n": "NOT_TO_EQUAL",
|
||||
"</a></foo >\n": "TO_EQUAL",
|
||||
"</a href=\"foo\">\n": "NOT_TO_EQUAL",
|
||||
"foo <!-- this is a\ncomment - with hyphen -->\n": "TO_EQUAL",
|
||||
"foo <!-- not a comment -- two hyphens -->\n": "NOT_TO_EQUAL",
|
||||
"foo <!--> foo -->\n\nfoo <!-- foo--->\n": "NOT_TO_EQUAL",
|
||||
"foo <?php echo $a; ?>\n": "TO_EQUAL",
|
||||
"foo <!ELEMENT br EMPTY>\n": "TO_EQUAL",
|
||||
"foo <![CDATA[>&<]]>\n": "NOT_TO_EQUAL",
|
||||
"foo <a href=\"ö\">\n": "TO_EQUAL",
|
||||
"foo <a href=\"\\*\">\n": "TO_EQUAL",
|
||||
"<a href=\"\\\"\">\n": "NOT_TO_EQUAL",
|
||||
"foo \nbaz\n": "NOT_TO_EQUAL",
|
||||
"foo\\\nbaz\n": "NOT_TO_EQUAL",
|
||||
"foo \nbaz\n": "NOT_TO_EQUAL",
|
||||
"foo \n bar\n": "NOT_TO_EQUAL",
|
||||
"foo\\\n bar\n": "NOT_TO_EQUAL",
|
||||
"*foo \nbar*\n": "NOT_TO_EQUAL",
|
||||
"*foo\\\nbar*\n": "NOT_TO_EQUAL",
|
||||
"`code \nspan`\n": "TO_EQUAL",
|
||||
"`code\\\nspan`\n": "TO_EQUAL",
|
||||
"<a href=\"foo \nbar\">\n": "TO_EQUAL",
|
||||
"<a href=\"foo\\\nbar\">\n": "TO_EQUAL",
|
||||
"foo\\\n": "TO_EQUAL",
|
||||
"foo \n": "TO_EQUAL",
|
||||
"### foo\\\n": "TO_EQUAL",
|
||||
"### foo \n": "TO_EQUAL",
|
||||
"foo\nbaz\n": "TO_EQUAL",
|
||||
"foo \n baz\n": "TO_EQUAL",
|
||||
"hello $.;'there\n": "TO_EQUAL",
|
||||
"Foo χρῆν\n": "TO_EQUAL",
|
||||
"Multiple spaces\n": "TO_EQUAL"
|
||||
}
|
||||
1
node_modules/decap-cms-widget-markdown/src/serializers/__tests__/__fixtures__/duplicate_marks_github_issue_3280.md
generated
vendored
Normal file
1
node_modules/decap-cms-widget-markdown/src/serializers/__tests__/__fixtures__/duplicate_marks_github_issue_3280.md
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
Fill to_*this*_mark, and your charge is but a penny; to_*this*_a penny more; and so on to the full glass—the Cape Horn measure, which you may gulp down for a shilling.\n\nUpon entering the place I found a number of young seamen gathered about a table, examining by a dim light divers specimens of_*skrimshander*.
|
||||
110
node_modules/decap-cms-widget-markdown/src/serializers/__tests__/commonmark.spec.js
generated
vendored
Normal file
110
node_modules/decap-cms-widget-markdown/src/serializers/__tests__/commonmark.spec.js
generated
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
import { flow } from 'lodash';
|
||||
import { tests as commonmarkSpec } from 'commonmark-spec';
|
||||
import * as commonmark from 'commonmark';
|
||||
|
||||
import { markdownToSlate, slateToMarkdown } from '../index.js';
|
||||
|
||||
const skips = [
|
||||
{
|
||||
number: [456],
|
||||
reason: 'Remark ¯\\_(ツ)_/¯',
|
||||
},
|
||||
{
|
||||
number: [416, 417, 424, 425, 426, 431, 457, 460, 462, 464, 467],
|
||||
reason: 'Remark does not support infinite (redundant) nested marks',
|
||||
},
|
||||
{
|
||||
number: [455, 469, 470, 471],
|
||||
reason: 'Remark parses the initial set of identical nested delimiters first',
|
||||
},
|
||||
{
|
||||
number: [473, 476, 478, 480],
|
||||
reason: 'we convert underscores to asterisks for strong/emphasis',
|
||||
},
|
||||
{ number: 490, reason: 'Remark strips pointy enclosing pointy brackets from link url' },
|
||||
{ number: 503, reason: 'Remark allows non-breaking space between link url and title' },
|
||||
{ number: 507, reason: 'Remark allows a space between link alt and url' },
|
||||
{
|
||||
number: [
|
||||
511, 516, 525, 528, 529, 530, 532, 533, 534, 540, 541, 542, 543, 546, 548, 560, 565, 567,
|
||||
],
|
||||
reason: 'we convert link references to standard links, but Remark also fails these',
|
||||
},
|
||||
{
|
||||
number: [569, 570, 571, 572, 573, 581, 585],
|
||||
reason: 'Remark does not recognize or remove marks in image alt text',
|
||||
},
|
||||
{ number: 589, reason: 'Remark does not honor backslash escape of image exclamation point' },
|
||||
{ number: 593, reason: 'Remark removes "mailto:" from autolink text' },
|
||||
{ number: 599, reason: 'Remark does not escape all expected entities' },
|
||||
{ number: 602, reason: 'Remark allows autolink emails to contain backslashes' },
|
||||
];
|
||||
|
||||
const onlys = [
|
||||
// just add the spec number, eg:
|
||||
// 431,
|
||||
];
|
||||
|
||||
/**
|
||||
* Each test receives input markdown and output html as expected for Commonmark
|
||||
* compliance. To test all of our handling in one go, we serialize the markdown
|
||||
* into our Slate AST, then back to raw markdown, and finally to HTML.
|
||||
*/
|
||||
const reader = new commonmark.Parser();
|
||||
const writer = new commonmark.HtmlRenderer();
|
||||
|
||||
function parseWithCommonmark(markdown) {
|
||||
const parsed = reader.parse(markdown);
|
||||
return writer.render(parsed);
|
||||
}
|
||||
|
||||
const parse = flow([markdownToSlate, slateToMarkdown]);
|
||||
|
||||
/**
|
||||
* Passing this test suite requires 100% Commonmark compliance. There are 624
|
||||
* tests, of which we're passing about 300 as of introduction of this suite. To
|
||||
* work on improving Commonmark support, update __fixtures__/commonmarkExpected.json
|
||||
*/
|
||||
describe.skip('Commonmark support', function () {
|
||||
const specs =
|
||||
onlys.length > 0
|
||||
? commonmarkSpec.filter(({ number }) => onlys.includes(number))
|
||||
: commonmarkSpec;
|
||||
specs.forEach(spec => {
|
||||
const skip = skips.find(({ number }) => {
|
||||
return Array.isArray(number) ? number.includes(spec.number) : number === spec.number;
|
||||
});
|
||||
const specUrl = `https://spec.commonmark.org/0.29/#example-${spec.number}`;
|
||||
const parsed = parse(spec.markdown);
|
||||
const commonmarkParsedHtml = parseWithCommonmark(parsed);
|
||||
const description = `
|
||||
${spec.section}
|
||||
${specUrl}
|
||||
|
||||
Spec:
|
||||
${JSON.stringify(spec, null, 2)}
|
||||
|
||||
Markdown input:
|
||||
${spec.markdown}
|
||||
|
||||
Markdown parsed through Slate/Remark and back to Markdown:
|
||||
${parsed}
|
||||
|
||||
HTML output:
|
||||
${commonmarkParsedHtml}
|
||||
|
||||
Expected HTML output:
|
||||
${spec.html}
|
||||
`;
|
||||
if (skip) {
|
||||
const showMessage = Array.isArray(skip.number) ? skip.number[0] === spec.number : true;
|
||||
if (showMessage) {
|
||||
//console.log(`skipping spec ${skip.number}\n${skip.reason}\n${specUrl}`);
|
||||
}
|
||||
}
|
||||
const testFn = skip ? test.skip : test;
|
||||
testFn(description, () => {
|
||||
expect(commonmarkParsedHtml).toEqual(spec.html);
|
||||
});
|
||||
});
|
||||
});
|
||||
52
node_modules/decap-cms-widget-markdown/src/serializers/__tests__/index.spec.js
generated
vendored
Normal file
52
node_modules/decap-cms-widget-markdown/src/serializers/__tests__/index.spec.js
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
|
||||
import { markdownToSlate, htmlToSlate } from '../';
|
||||
|
||||
describe('markdownToSlate', () => {
|
||||
it('should not add duplicate identical marks under the same node (GitHub Issue 3280)', () => {
|
||||
const mdast = fs.readFileSync(
|
||||
path.join(__dirname, '__fixtures__', 'duplicate_marks_github_issue_3280.md'),
|
||||
);
|
||||
const slate = markdownToSlate(mdast);
|
||||
|
||||
expect(slate).toEqual([
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [
|
||||
{
|
||||
text: 'Fill to',
|
||||
},
|
||||
{
|
||||
italic: true,
|
||||
marks: [{ type: 'italic' }],
|
||||
text: 'this_mark, and your charge is but a penny; tothisa penny more; and so on to the full glass—the Cape Horn measure, which you may gulp down for a shilling.\\n\\nUpon entering the place I found a number of young seamen gathered about a table, examining by a dim light divers specimens ofskrimshander',
|
||||
},
|
||||
{
|
||||
text: '.',
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('htmlToSlate', () => {
|
||||
it('should preserve spaces in rich html (GitHub Issue 3727)', () => {
|
||||
const html = `<strong>Bold Text</strong><span><span> </span>regular text<span> </span></span>`;
|
||||
|
||||
const actual = htmlToSlate(html);
|
||||
expect(actual).toEqual({
|
||||
type: 'root',
|
||||
children: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [
|
||||
{ text: 'Bold Text', bold: true, marks: [{ type: 'bold' }] },
|
||||
{ text: ' regular text' },
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
25
node_modules/decap-cms-widget-markdown/src/serializers/__tests__/remarkAllowHtmlEntities.spec.js
generated
vendored
Normal file
25
node_modules/decap-cms-widget-markdown/src/serializers/__tests__/remarkAllowHtmlEntities.spec.js
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
import unified from 'unified';
|
||||
import markdownToRemark from 'remark-parse';
|
||||
|
||||
import remarkAllowHtmlEntities from '../remarkAllowHtmlEntities';
|
||||
|
||||
function process(markdown) {
|
||||
const mdast = unified().use(markdownToRemark).use(remarkAllowHtmlEntities).parse(markdown);
|
||||
|
||||
/**
|
||||
* The MDAST will look like:
|
||||
*
|
||||
* { type: 'root', children: [
|
||||
* { type: 'paragraph', children: [
|
||||
* // results here
|
||||
* ]}
|
||||
* ]}
|
||||
*/
|
||||
return mdast.children[0].children[0].value;
|
||||
}
|
||||
|
||||
describe('remarkAllowHtmlEntities', () => {
|
||||
it('should not decode HTML entities', () => {
|
||||
expect(process('<div>')).toEqual('<div>');
|
||||
});
|
||||
});
|
||||
171
node_modules/decap-cms-widget-markdown/src/serializers/__tests__/remarkAssertParents.spec.js
generated
vendored
Normal file
171
node_modules/decap-cms-widget-markdown/src/serializers/__tests__/remarkAssertParents.spec.js
generated
vendored
Normal file
@@ -0,0 +1,171 @@
|
||||
import u from 'unist-builder';
|
||||
|
||||
import remarkAssertParents from '../remarkAssertParents';
|
||||
|
||||
const transform = remarkAssertParents();
|
||||
|
||||
describe('remarkAssertParents', () => {
|
||||
it('should unnest invalidly nested blocks', () => {
|
||||
const input = u('root', [
|
||||
u('paragraph', [
|
||||
u('paragraph', [u('text', 'Paragraph text.')]),
|
||||
u('heading', { depth: 1 }, [u('text', 'Heading text.')]),
|
||||
u('code', 'someCode()'),
|
||||
u('blockquote', [u('text', 'Quote text.')]),
|
||||
u('list', [u('listItem', [u('text', 'A list item.')])]),
|
||||
u('table', [u('tableRow', [u('tableCell', [u('text', 'Text in a table cell.')])])]),
|
||||
u('thematicBreak'),
|
||||
]),
|
||||
]);
|
||||
|
||||
const output = u('root', [
|
||||
u('paragraph', [u('text', 'Paragraph text.')]),
|
||||
u('heading', { depth: 1 }, [u('text', 'Heading text.')]),
|
||||
u('code', 'someCode()'),
|
||||
u('blockquote', [u('text', 'Quote text.')]),
|
||||
u('list', [u('listItem', [u('text', 'A list item.')])]),
|
||||
u('table', [u('tableRow', [u('tableCell', [u('text', 'Text in a table cell.')])])]),
|
||||
u('thematicBreak'),
|
||||
]);
|
||||
|
||||
expect(transform(input)).toEqual(output);
|
||||
});
|
||||
|
||||
it('should unnest deeply nested blocks', () => {
|
||||
const input = u('root', [
|
||||
u('paragraph', [
|
||||
u('paragraph', [
|
||||
u('paragraph', [
|
||||
u('paragraph', [u('text', 'Paragraph text.')]),
|
||||
u('heading', { depth: 1 }, [u('text', 'Heading text.')]),
|
||||
u('code', 'someCode()'),
|
||||
u('blockquote', [
|
||||
u('paragraph', [u('strong', [u('heading', [u('text', 'Quote text.')])])]),
|
||||
]),
|
||||
u('list', [u('listItem', [u('text', 'A list item.')])]),
|
||||
u('table', [u('tableRow', [u('tableCell', [u('text', 'Text in a table cell.')])])]),
|
||||
u('thematicBreak'),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]);
|
||||
|
||||
const output = u('root', [
|
||||
u('paragraph', [u('text', 'Paragraph text.')]),
|
||||
u('heading', { depth: 1 }, [u('text', 'Heading text.')]),
|
||||
u('code', 'someCode()'),
|
||||
u('blockquote', [u('heading', [u('text', 'Quote text.')])]),
|
||||
u('list', [u('listItem', [u('text', 'A list item.')])]),
|
||||
u('table', [u('tableRow', [u('tableCell', [u('text', 'Text in a table cell.')])])]),
|
||||
u('thematicBreak'),
|
||||
]);
|
||||
|
||||
expect(transform(input)).toEqual(output);
|
||||
});
|
||||
|
||||
it('should remove blocks that are emptied as a result of denesting', () => {
|
||||
const input = u('root', [
|
||||
u('paragraph', [u('heading', { depth: 1 }, [u('text', 'Heading text.')])]),
|
||||
]);
|
||||
|
||||
const output = u('root', [u('heading', { depth: 1 }, [u('text', 'Heading text.')])]);
|
||||
|
||||
expect(transform(input)).toEqual(output);
|
||||
});
|
||||
|
||||
it('should remove blocks that are emptied as a result of denesting', () => {
|
||||
const input = u('root', [
|
||||
u('paragraph', [u('heading', { depth: 1 }, [u('text', 'Heading text.')])]),
|
||||
]);
|
||||
|
||||
const output = u('root', [u('heading', { depth: 1 }, [u('text', 'Heading text.')])]);
|
||||
|
||||
expect(transform(input)).toEqual(output);
|
||||
});
|
||||
|
||||
it('should handle asymmetrical splits', () => {
|
||||
const input = u('root', [
|
||||
u('paragraph', [u('heading', { depth: 1 }, [u('text', 'Heading text.')])]),
|
||||
]);
|
||||
|
||||
const output = u('root', [u('heading', { depth: 1 }, [u('text', 'Heading text.')])]);
|
||||
|
||||
expect(transform(input)).toEqual(output);
|
||||
});
|
||||
|
||||
it('should nest invalidly nested blocks in the nearest valid ancestor', () => {
|
||||
const input = u('root', [
|
||||
u('paragraph', [
|
||||
u('blockquote', [u('strong', [u('heading', { depth: 1 }, [u('text', 'Heading text.')])])]),
|
||||
]),
|
||||
]);
|
||||
|
||||
const output = u('root', [
|
||||
u('blockquote', [u('heading', { depth: 1 }, [u('text', 'Heading text.')])]),
|
||||
]);
|
||||
|
||||
expect(transform(input)).toEqual(output);
|
||||
});
|
||||
|
||||
it('should preserve validly nested siblings of invalidly nested blocks', () => {
|
||||
const input = u('root', [
|
||||
u('paragraph', [
|
||||
u('blockquote', [
|
||||
u('strong', [
|
||||
u('text', 'Deep validly nested text a.'),
|
||||
u('heading', { depth: 1 }, [u('text', 'Heading text.')]),
|
||||
u('text', 'Deep validly nested text b.'),
|
||||
]),
|
||||
]),
|
||||
u('text', 'Validly nested text.'),
|
||||
]),
|
||||
]);
|
||||
|
||||
const output = u('root', [
|
||||
u('blockquote', [
|
||||
u('strong', [u('text', 'Deep validly nested text a.')]),
|
||||
u('heading', { depth: 1 }, [u('text', 'Heading text.')]),
|
||||
u('strong', [u('text', 'Deep validly nested text b.')]),
|
||||
]),
|
||||
u('paragraph', [u('text', 'Validly nested text.')]),
|
||||
]);
|
||||
|
||||
expect(transform(input)).toEqual(output);
|
||||
});
|
||||
|
||||
it('should allow intermediate parents like list and table to contain required block children', () => {
|
||||
const input = u('root', [
|
||||
u('blockquote', [
|
||||
u('list', [
|
||||
u('listItem', [
|
||||
u('table', [
|
||||
u('tableRow', [
|
||||
u('tableCell', [
|
||||
u('heading', { depth: 1 }, [u('text', 'Validly nested heading text.')]),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]);
|
||||
|
||||
const output = u('root', [
|
||||
u('blockquote', [
|
||||
u('list', [
|
||||
u('listItem', [
|
||||
u('table', [
|
||||
u('tableRow', [
|
||||
u('tableCell', [
|
||||
u('heading', { depth: 1 }, [u('text', 'Validly nested heading text.')]),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]);
|
||||
|
||||
expect(transform(input)).toEqual(output);
|
||||
});
|
||||
});
|
||||
84
node_modules/decap-cms-widget-markdown/src/serializers/__tests__/remarkEscapeMarkdownEntities.spec.js
generated
vendored
Normal file
84
node_modules/decap-cms-widget-markdown/src/serializers/__tests__/remarkEscapeMarkdownEntities.spec.js
generated
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
import unified from 'unified';
|
||||
import u from 'unist-builder';
|
||||
|
||||
import remarkEscapeMarkdownEntities from '../remarkEscapeMarkdownEntities';
|
||||
|
||||
function process(text) {
|
||||
const tree = u('root', [u('text', text)]);
|
||||
const escapedMdast = unified().use(remarkEscapeMarkdownEntities).runSync(tree);
|
||||
|
||||
return escapedMdast.children[0].value;
|
||||
}
|
||||
|
||||
describe('remarkEscapeMarkdownEntities', () => {
|
||||
it('should escape common markdown entities', () => {
|
||||
expect(process('*a*')).toEqual('\\*a\\*');
|
||||
expect(process('**a**')).toEqual('\\*\\*a\\*\\*');
|
||||
expect(process('***a***')).toEqual('\\*\\*\\*a\\*\\*\\*');
|
||||
expect(process('_a_')).toEqual('\\_a\\_');
|
||||
expect(process('__a__')).toEqual('\\_\\_a\\_\\_');
|
||||
expect(process('~~a~~')).toEqual('\\~\\~a\\~\\~');
|
||||
expect(process('[]')).toEqual('\\[]');
|
||||
expect(process('[]()')).toEqual('\\[]()');
|
||||
expect(process('[a](b)')).toEqual('\\[a](b)');
|
||||
expect(process('[Test sentence.](https://www.example.com)')).toEqual(
|
||||
'\\[Test sentence.](https://www.example.com)',
|
||||
);
|
||||
expect(process('')).toEqual('!\\[a](b)');
|
||||
});
|
||||
|
||||
it('should not escape inactive, single markdown entities', () => {
|
||||
expect(process('a*b')).toEqual('a*b');
|
||||
expect(process('_')).toEqual('_');
|
||||
expect(process('~')).toEqual('~');
|
||||
expect(process('[')).toEqual('[');
|
||||
});
|
||||
|
||||
it('should escape leading markdown entities', () => {
|
||||
expect(process('#')).toEqual('\\#');
|
||||
expect(process('-')).toEqual('\\-');
|
||||
expect(process('*')).toEqual('\\*');
|
||||
expect(process('>')).toEqual('\\>');
|
||||
expect(process('=')).toEqual('\\=');
|
||||
expect(process('|')).toEqual('\\|');
|
||||
expect(process('```')).toEqual('\\`\\``');
|
||||
expect(process(' ')).toEqual('\\ ');
|
||||
});
|
||||
|
||||
it('should escape leading markdown entities preceded by whitespace', () => {
|
||||
expect(process('\n #')).toEqual('\\#');
|
||||
expect(process(' \n-')).toEqual('\\-');
|
||||
});
|
||||
|
||||
it('should not escape leading markdown entities preceded by non-whitespace characters', () => {
|
||||
expect(process('a# # b #')).toEqual('a# # b #');
|
||||
expect(process('a- - b -')).toEqual('a- - b -');
|
||||
});
|
||||
|
||||
it('should not escape html tags', () => {
|
||||
expect(process('<a attr="**a**">')).toEqual('<a attr="**a**">');
|
||||
expect(process('a b <c attr="**d**"> e')).toEqual('a b <c attr="**d**"> e');
|
||||
});
|
||||
|
||||
it('should escape the contents of html blocks', () => {
|
||||
expect(process('<div>*a*</div>')).toEqual('<div>\\*a\\*</div>');
|
||||
});
|
||||
|
||||
it('should not escape the contents of preformatted html blocks', () => {
|
||||
expect(process('<pre>*a*</pre>')).toEqual('<pre>*a*</pre>');
|
||||
expect(process('<script>*a*</script>')).toEqual('<script>*a*</script>');
|
||||
expect(process('<style>*a*</style>')).toEqual('<style>*a*</style>');
|
||||
expect(process('<pre>\n*a*\n</pre>')).toEqual('<pre>\n*a*\n</pre>');
|
||||
expect(process('a b <pre>*c*</pre> d e')).toEqual('a b <pre>*c*</pre> d e');
|
||||
});
|
||||
|
||||
it('should not escape footnote references', () => {
|
||||
expect(process('[^a]')).toEqual('[^a]');
|
||||
expect(process('[^1]')).toEqual('[^1]');
|
||||
});
|
||||
|
||||
it('should not escape footnotes', () => {
|
||||
expect(process('[^a]:')).toEqual('[^a]:');
|
||||
expect(process('[^1]:')).toEqual('[^1]:');
|
||||
});
|
||||
});
|
||||
43
node_modules/decap-cms-widget-markdown/src/serializers/__tests__/remarkPaddedLinks.spec.js
generated
vendored
Normal file
43
node_modules/decap-cms-widget-markdown/src/serializers/__tests__/remarkPaddedLinks.spec.js
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
import unified from 'unified';
|
||||
import markdownToRemark from 'remark-parse';
|
||||
import remarkToMarkdown from 'remark-stringify';
|
||||
|
||||
import remarkPaddedLinks from '../remarkPaddedLinks';
|
||||
|
||||
function input(markdown) {
|
||||
return unified()
|
||||
.use(markdownToRemark)
|
||||
.use(remarkPaddedLinks)
|
||||
.use(remarkToMarkdown)
|
||||
.processSync(markdown).contents;
|
||||
}
|
||||
|
||||
function output(markdown) {
|
||||
return unified().use(markdownToRemark).use(remarkToMarkdown).processSync(markdown).contents;
|
||||
}
|
||||
|
||||
describe('remarkPaddedLinks', () => {
|
||||
it('should move leading and trailing spaces outside of a link', () => {
|
||||
expect(input('[ a ](b)')).toEqual(output(' [a](b) '));
|
||||
});
|
||||
|
||||
it('should convert multiple leading or trailing spaces to a single space', () => {
|
||||
expect(input('[ a ](b)')).toEqual(output(' [a](b) '));
|
||||
});
|
||||
|
||||
it('should work with only a leading space or only a trailing space', () => {
|
||||
expect(input('[ a](b)[c ](d)')).toEqual(output(' [a](b)[c](d) '));
|
||||
});
|
||||
|
||||
it('should work for nested links', () => {
|
||||
expect(input('* # a[ b ](c)d')).toEqual(output('* # a [b](c) d'));
|
||||
});
|
||||
|
||||
it('should work for parents with multiple links that are not siblings', () => {
|
||||
expect(input('# a[ b ](c)d **[ e ](f)**')).toEqual(output('# a [b](c) d ** [e](f) **'));
|
||||
});
|
||||
|
||||
it('should work for links with arbitrarily nested children', () => {
|
||||
expect(input('[ a __*b*__ _c_ ](d)')).toEqual(output(' [a __*b*__ _c_](d) '));
|
||||
});
|
||||
});
|
||||
299
node_modules/decap-cms-widget-markdown/src/serializers/__tests__/remarkPlugins.spec.js
generated
vendored
Normal file
299
node_modules/decap-cms-widget-markdown/src/serializers/__tests__/remarkPlugins.spec.js
generated
vendored
Normal file
@@ -0,0 +1,299 @@
|
||||
import visit from 'unist-util-visit';
|
||||
|
||||
import { markdownToRemark, remarkToMarkdown } from '..';
|
||||
|
||||
describe('registered remark plugins', () => {
|
||||
function withNetlifyLinks() {
|
||||
return function transformer(tree) {
|
||||
visit(tree, 'link', function onLink(node) {
|
||||
node.url = 'https://netlify.com';
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
it('should use remark transformer plugins when converting mdast to markdown', () => {
|
||||
const plugins = [withNetlifyLinks];
|
||||
const result = remarkToMarkdown(
|
||||
{
|
||||
type: 'root',
|
||||
children: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [
|
||||
{
|
||||
type: 'text',
|
||||
value: 'Some ',
|
||||
},
|
||||
{
|
||||
type: 'emphasis',
|
||||
children: [
|
||||
{
|
||||
type: 'text',
|
||||
value: 'important',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
value: ' text with ',
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
title: null,
|
||||
url: 'https://this-value-should-be-replaced.com',
|
||||
children: [
|
||||
{
|
||||
type: 'text',
|
||||
value: 'a link',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
value: ' in it.',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins,
|
||||
);
|
||||
expect(result).toMatchInlineSnapshot(
|
||||
`"Some *important* text with [a link](https://netlify.com) in it."`,
|
||||
);
|
||||
});
|
||||
|
||||
it('should use remark transformer plugins when converting markdown to mdast', () => {
|
||||
const plugins = [withNetlifyLinks];
|
||||
const result = markdownToRemark(
|
||||
'Some text with [a link](https://this-value-should-be-replaced.com) in it.',
|
||||
plugins,
|
||||
);
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"children": Array [],
|
||||
"position": Position {
|
||||
"end": Object {
|
||||
"column": 16,
|
||||
"line": 1,
|
||||
"offset": 15,
|
||||
},
|
||||
"indent": Array [],
|
||||
"start": Object {
|
||||
"column": 1,
|
||||
"line": 1,
|
||||
"offset": 0,
|
||||
},
|
||||
},
|
||||
"type": "text",
|
||||
"value": "Some text with ",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"children": Array [],
|
||||
"position": Position {
|
||||
"end": Object {
|
||||
"column": 23,
|
||||
"line": 1,
|
||||
"offset": 22,
|
||||
},
|
||||
"indent": Array [],
|
||||
"start": Object {
|
||||
"column": 17,
|
||||
"line": 1,
|
||||
"offset": 16,
|
||||
},
|
||||
},
|
||||
"type": "text",
|
||||
"value": "a link",
|
||||
},
|
||||
],
|
||||
"position": Position {
|
||||
"end": Object {
|
||||
"column": 67,
|
||||
"line": 1,
|
||||
"offset": 66,
|
||||
},
|
||||
"indent": Array [],
|
||||
"start": Object {
|
||||
"column": 16,
|
||||
"line": 1,
|
||||
"offset": 15,
|
||||
},
|
||||
},
|
||||
"title": null,
|
||||
"type": "link",
|
||||
"url": "https://netlify.com",
|
||||
},
|
||||
Object {
|
||||
"children": Array [],
|
||||
"position": Position {
|
||||
"end": Object {
|
||||
"column": 74,
|
||||
"line": 1,
|
||||
"offset": 73,
|
||||
},
|
||||
"indent": Array [],
|
||||
"start": Object {
|
||||
"column": 67,
|
||||
"line": 1,
|
||||
"offset": 66,
|
||||
},
|
||||
},
|
||||
"type": "text",
|
||||
"value": " in it.",
|
||||
},
|
||||
],
|
||||
"position": Position {
|
||||
"end": Object {
|
||||
"column": 74,
|
||||
"line": 1,
|
||||
"offset": 73,
|
||||
},
|
||||
"indent": Array [],
|
||||
"start": Object {
|
||||
"column": 1,
|
||||
"line": 1,
|
||||
"offset": 0,
|
||||
},
|
||||
},
|
||||
"type": "paragraph",
|
||||
},
|
||||
],
|
||||
"position": Object {
|
||||
"end": Object {
|
||||
"column": 74,
|
||||
"line": 1,
|
||||
"offset": 73,
|
||||
},
|
||||
"start": Object {
|
||||
"column": 1,
|
||||
"line": 1,
|
||||
"offset": 0,
|
||||
},
|
||||
},
|
||||
"type": "root",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should use remark serializer plugins when converting mdast to markdown', () => {
|
||||
function withEscapedLessThanChar() {
|
||||
if (this.Compiler) {
|
||||
this.Compiler.prototype.visitors.text = node => {
|
||||
return node.value.replace(/</g, '<');
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const plugins = [withEscapedLessThanChar];
|
||||
const result = remarkToMarkdown(
|
||||
{
|
||||
type: 'root',
|
||||
children: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [
|
||||
{
|
||||
type: 'text',
|
||||
value: '<3 Netlify',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins,
|
||||
);
|
||||
expect(result).toMatchInlineSnapshot(`"<3 Netlify"`);
|
||||
});
|
||||
|
||||
it('should use remark preset with settings when converting mdast to markdown', () => {
|
||||
const settings = {
|
||||
emphasis: '_',
|
||||
bullet: '-',
|
||||
};
|
||||
|
||||
const plugins = [{ settings }];
|
||||
const result = remarkToMarkdown(
|
||||
{
|
||||
type: 'root',
|
||||
children: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [
|
||||
{
|
||||
type: 'text',
|
||||
value: 'Some ',
|
||||
},
|
||||
{
|
||||
type: 'emphasis',
|
||||
children: [
|
||||
{
|
||||
type: 'text',
|
||||
value: 'important',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
value: ' points:',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
ordered: false,
|
||||
start: null,
|
||||
spread: false,
|
||||
children: [
|
||||
{
|
||||
type: 'listItem',
|
||||
spread: false,
|
||||
checked: null,
|
||||
children: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [
|
||||
{
|
||||
type: 'text',
|
||||
value: 'One',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'listItem',
|
||||
spread: false,
|
||||
checked: null,
|
||||
children: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [
|
||||
{
|
||||
type: 'text',
|
||||
value: 'Two',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins,
|
||||
);
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"Some _important_ points:
|
||||
|
||||
- One
|
||||
- Two"
|
||||
`);
|
||||
});
|
||||
});
|
||||
106
node_modules/decap-cms-widget-markdown/src/serializers/__tests__/remarkShortcodes.spec.js
generated
vendored
Normal file
106
node_modules/decap-cms-widget-markdown/src/serializers/__tests__/remarkShortcodes.spec.js
generated
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
import { Map, OrderedMap } from 'immutable';
|
||||
|
||||
import { remarkParseShortcodes, getLinesWithOffsets } from '../remarkShortcodes';
|
||||
|
||||
// Stub of Remark Parser
|
||||
function process(value, plugins, processEat = () => {}) {
|
||||
function eat() {
|
||||
return processEat;
|
||||
}
|
||||
|
||||
function Parser() {}
|
||||
Parser.prototype.blockTokenizers = {};
|
||||
Parser.prototype.blockMethods = [];
|
||||
remarkParseShortcodes.call({ Parser }, { plugins });
|
||||
Parser.prototype.blockTokenizers.shortcode(eat, value);
|
||||
}
|
||||
|
||||
function EditorComponent({ id = 'foo', fromBlock = jest.fn(), pattern }) {
|
||||
return {
|
||||
id,
|
||||
fromBlock,
|
||||
pattern,
|
||||
};
|
||||
}
|
||||
|
||||
describe('remarkParseShortcodes', () => {
|
||||
describe('pattern matching', () => {
|
||||
it('should work', () => {
|
||||
const editorComponent = EditorComponent({ pattern: /bar/ });
|
||||
process('foo bar', Map({ [editorComponent.id]: editorComponent }));
|
||||
expect(editorComponent.fromBlock).toHaveBeenCalledWith(expect.arrayContaining(['bar']));
|
||||
});
|
||||
it('should match value surrounded in newlines', () => {
|
||||
const editorComponent = EditorComponent({ pattern: /^bar$/ });
|
||||
process('foo\n\nbar\n', Map({ [editorComponent.id]: editorComponent }));
|
||||
expect(editorComponent.fromBlock).toHaveBeenCalledWith(expect.arrayContaining(['bar']));
|
||||
});
|
||||
it('should match multiline shortcodes', () => {
|
||||
const editorComponent = EditorComponent({ pattern: /^foo\nbar$/ });
|
||||
process('foo\nbar', Map({ [editorComponent.id]: editorComponent }));
|
||||
expect(editorComponent.fromBlock).toHaveBeenCalledWith(expect.arrayContaining(['foo\nbar']));
|
||||
});
|
||||
it('should match multiline shortcodes with empty lines', () => {
|
||||
const editorComponent = EditorComponent({ pattern: /^foo\n\nbar$/ });
|
||||
process('foo\n\nbar', Map({ [editorComponent.id]: editorComponent }));
|
||||
expect(editorComponent.fromBlock).toHaveBeenCalledWith(
|
||||
expect.arrayContaining(['foo\n\nbar']),
|
||||
);
|
||||
});
|
||||
it('should match shortcodes based on order of occurrence in value', () => {
|
||||
const fooEditorComponent = EditorComponent({ id: 'foo', pattern: /foo/ });
|
||||
const barEditorComponent = EditorComponent({ id: 'bar', pattern: /bar/ });
|
||||
process(
|
||||
'foo\n\nbar',
|
||||
OrderedMap([
|
||||
[barEditorComponent.id, barEditorComponent],
|
||||
[fooEditorComponent.id, fooEditorComponent],
|
||||
]),
|
||||
);
|
||||
expect(fooEditorComponent.fromBlock).toHaveBeenCalledWith(expect.arrayContaining(['foo']));
|
||||
});
|
||||
it('should match shortcodes based on order of occurrence in value even when some use line anchors', () => {
|
||||
const barEditorComponent = EditorComponent({ id: 'bar', pattern: /bar/ });
|
||||
const bazEditorComponent = EditorComponent({ id: 'baz', pattern: /^baz$/ });
|
||||
process(
|
||||
'foo\n\nbar\n\nbaz',
|
||||
OrderedMap([
|
||||
[bazEditorComponent.id, bazEditorComponent],
|
||||
[barEditorComponent.id, barEditorComponent],
|
||||
]),
|
||||
);
|
||||
expect(barEditorComponent.fromBlock).toHaveBeenCalledWith(expect.arrayContaining(['bar']));
|
||||
});
|
||||
});
|
||||
describe('output', () => {
|
||||
it('should be a remark shortcode node', () => {
|
||||
const processEat = jest.fn();
|
||||
const shortcodeData = { bar: 'baz' };
|
||||
const expectedNode = { type: 'shortcode', data: { shortcode: 'foo', shortcodeData } };
|
||||
const editorComponent = EditorComponent({ pattern: /bar/, fromBlock: () => shortcodeData });
|
||||
process('foo bar', Map({ [editorComponent.id]: editorComponent }), processEat);
|
||||
expect(processEat).toHaveBeenCalledWith(expectedNode);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getLinesWithOffsets', () => {
|
||||
test('should split into lines', () => {
|
||||
const value = ' line1\n\nline2 \n\n line3 \n\n';
|
||||
|
||||
const lines = getLinesWithOffsets(value);
|
||||
expect(lines).toEqual([
|
||||
{ line: ' line1', start: 0 },
|
||||
{ line: 'line2', start: 8 },
|
||||
{ line: ' line3', start: 16 },
|
||||
{ line: '', start: 30 },
|
||||
]);
|
||||
});
|
||||
|
||||
test('should return single item on no match', () => {
|
||||
const value = ' line1 ';
|
||||
|
||||
const lines = getLinesWithOffsets(value);
|
||||
expect(lines).toEqual([{ line: ' line1', start: 0 }]);
|
||||
});
|
||||
});
|
||||
67
node_modules/decap-cms-widget-markdown/src/serializers/__tests__/remarkSlate.spec.js
generated
vendored
Normal file
67
node_modules/decap-cms-widget-markdown/src/serializers/__tests__/remarkSlate.spec.js
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
import { mergeAdjacentTexts } from '../remarkSlate';
|
||||
describe('remarkSlate', () => {
|
||||
describe('mergeAdjacentTexts', () => {
|
||||
it('should handle empty array', () => {
|
||||
const children = [];
|
||||
expect(mergeAdjacentTexts(children)).toBe(children);
|
||||
});
|
||||
|
||||
it('should merge adjacent texts with same marks', () => {
|
||||
const children = [
|
||||
{ text: '<a href="https://www.netlify.com" target="_blank">', marks: [] },
|
||||
{ text: 'Netlify', marks: [] },
|
||||
{ text: '</a>', marks: [] },
|
||||
];
|
||||
|
||||
expect(mergeAdjacentTexts(children)).toEqual([
|
||||
{
|
||||
text: '<a href="https://www.netlify.com" target="_blank">Netlify</a>',
|
||||
marks: [],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should not merge adjacent texts with different marks', () => {
|
||||
const children = [
|
||||
{ text: '<a href="https://www.netlify.com" target="_blank">', marks: [] },
|
||||
{ text: 'Netlify', marks: ['b'] },
|
||||
{ text: '</a>', marks: [] },
|
||||
];
|
||||
|
||||
expect(mergeAdjacentTexts(children)).toEqual(children);
|
||||
});
|
||||
|
||||
it('should handle mixed children array', () => {
|
||||
const children = [
|
||||
{ object: 'inline' },
|
||||
{ text: '<a href="https://www.netlify.com" target="_blank">', marks: [] },
|
||||
{ text: 'Netlify', marks: [] },
|
||||
{ text: '</a>', marks: [] },
|
||||
{ object: 'inline' },
|
||||
{ text: '<a href="https://www.netlify.com" target="_blank">', marks: [] },
|
||||
{ text: 'Netlify', marks: ['b'] },
|
||||
{ text: '</a>', marks: [] },
|
||||
{ text: '<a href="https://www.netlify.com" target="_blank">', marks: [] },
|
||||
{ object: 'inline' },
|
||||
{ text: '</a>', marks: [] },
|
||||
];
|
||||
|
||||
expect(mergeAdjacentTexts(children)).toEqual([
|
||||
{ object: 'inline' },
|
||||
{
|
||||
text: '<a href="https://www.netlify.com" target="_blank">Netlify</a>',
|
||||
marks: [],
|
||||
},
|
||||
{ object: 'inline' },
|
||||
{ text: '<a href="https://www.netlify.com" target="_blank">', marks: [] },
|
||||
{ text: 'Netlify', marks: ['b'] },
|
||||
{
|
||||
text: '</a><a href="https://www.netlify.com" target="_blank">',
|
||||
marks: [],
|
||||
},
|
||||
{ object: 'inline' },
|
||||
{ text: '</a>', marks: [] },
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
23
node_modules/decap-cms-widget-markdown/src/serializers/__tests__/remarkStripTrailingBreaks.spec.js
generated
vendored
Normal file
23
node_modules/decap-cms-widget-markdown/src/serializers/__tests__/remarkStripTrailingBreaks.spec.js
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
import unified from 'unified';
|
||||
import u from 'unist-builder';
|
||||
|
||||
import remarkStripTrailingBreaks from '../remarkStripTrailingBreaks';
|
||||
|
||||
function process(children) {
|
||||
const tree = u('root', children);
|
||||
const strippedMdast = unified().use(remarkStripTrailingBreaks).runSync(tree);
|
||||
|
||||
return strippedMdast.children;
|
||||
}
|
||||
|
||||
describe('remarkStripTrailingBreaks', () => {
|
||||
it('should remove trailing breaks at the end of a block', () => {
|
||||
expect(process([u('break')])).toEqual([]);
|
||||
expect(process([u('break'), u('text', '\n \n')])).toEqual([u('text', '\n \n')]);
|
||||
expect(process([u('text', 'a'), u('break')])).toEqual([u('text', 'a')]);
|
||||
});
|
||||
|
||||
it('should not remove trailing breaks that are not at the end of a block', () => {
|
||||
expect(process([u('break'), u('text', 'a')])).toEqual([u('break'), u('text', 'a')]);
|
||||
});
|
||||
});
|
||||
300
node_modules/decap-cms-widget-markdown/src/serializers/__tests__/slate.spec.js
generated
vendored
Normal file
300
node_modules/decap-cms-widget-markdown/src/serializers/__tests__/slate.spec.js
generated
vendored
Normal file
@@ -0,0 +1,300 @@
|
||||
/** @jsx h */
|
||||
|
||||
import { flow } from 'lodash';
|
||||
|
||||
import h from '../../../test-helpers/h';
|
||||
import { markdownToSlate, slateToMarkdown } from '../index';
|
||||
|
||||
const process = flow([markdownToSlate, slateToMarkdown]);
|
||||
|
||||
describe('slate', () => {
|
||||
it('should not decode encoded html entities in inline code', () => {
|
||||
expect(process('<element type="code"><div></element>')).toEqual(
|
||||
'<element type="code"><div></element>',
|
||||
);
|
||||
});
|
||||
|
||||
it('should parse non-text children of mark nodes', () => {
|
||||
expect(process('**a[b](c)d**')).toEqual('**a[b](c)d**');
|
||||
expect(process('**[a](b)**')).toEqual('**[a](b)**');
|
||||
expect(process('****')).toEqual('****');
|
||||
expect(process('_`a`_')).toEqual('*`a`*');
|
||||
});
|
||||
|
||||
it('should handle unstyled code nodes adjacent to styled code nodes', () => {
|
||||
expect(process('`foo`***`bar`***')).toEqual('`foo`***`bar`***');
|
||||
});
|
||||
|
||||
it('should handle styled code nodes adjacent to non-code text', () => {
|
||||
expect(process('_`a`b_')).toEqual('*`a`b*');
|
||||
expect(process('_`a`**b**_')).toEqual('*`a`**b***');
|
||||
});
|
||||
|
||||
it('should condense adjacent, identically styled text and inline nodes', () => {
|
||||
expect(process('**a ~~b~~~~c~~**')).toEqual('**a ~~bc~~**');
|
||||
expect(process('**a ~~b~~~~[c](d)~~**')).toEqual('**a ~~b[c](d)~~**');
|
||||
});
|
||||
|
||||
it('should handle nested markdown entities', () => {
|
||||
expect(process('**a**b**c**')).toEqual('**a**b**c**');
|
||||
expect(process('**a _b_ c**')).toEqual('**a *b* c**');
|
||||
expect(process('*`a`*')).toEqual('*`a`*');
|
||||
});
|
||||
|
||||
it('should parse inline images as images', () => {
|
||||
expect(process('a ')).toEqual('a ');
|
||||
});
|
||||
|
||||
it('should not escape markdown entities in html', () => {
|
||||
expect(process('<span>*</span>')).toEqual('<span>*</span>');
|
||||
});
|
||||
|
||||
it('should wrap break tags in surrounding marks', () => {
|
||||
expect(process('*a \nb*')).toEqual('*a\\\nb*');
|
||||
});
|
||||
|
||||
// slateAst no longer valid
|
||||
|
||||
it('should not output empty headers in markdown', () => {
|
||||
// prettier-ignore
|
||||
const slateAst = (
|
||||
<editor>
|
||||
<element type="heading-one"></element>
|
||||
<element type="paragraph">foo</element>
|
||||
<element type="heading-one"></element>
|
||||
|
||||
</editor>
|
||||
);
|
||||
expect(slateToMarkdown(slateAst.children)).toMatchInlineSnapshot(`"foo"`);
|
||||
});
|
||||
|
||||
it('should not output empty marks in markdown', () => {
|
||||
// prettier-ignore
|
||||
const slateAst = (
|
||||
<editor>
|
||||
<element type="paragraph">
|
||||
<text bold></text>
|
||||
foo<text italic><text bold></text></text>bar
|
||||
<text bold></text>baz<text italic></text>
|
||||
</element>
|
||||
</editor>
|
||||
);
|
||||
expect(slateToMarkdown(slateAst.children)).toMatchInlineSnapshot(`"foobarbaz"`);
|
||||
});
|
||||
|
||||
it('should not produce invalid markdown when a styled block has trailing whitespace', () => {
|
||||
// prettier-ignore
|
||||
const slateAst = (
|
||||
<editor>
|
||||
<element type="paragraph">
|
||||
<text bold>foo </text>bar <text bold>bim </text><text bold><text italic>bam</text></text>
|
||||
</element>
|
||||
</editor>
|
||||
);
|
||||
expect(slateToMarkdown(slateAst.children)).toMatchInlineSnapshot(`"**foo** bar **bim *bam***"`);
|
||||
});
|
||||
|
||||
it('should not produce invalid markdown when a styled block has leading whitespace', () => {
|
||||
// prettier-ignore
|
||||
const slateAst = (
|
||||
<editor>
|
||||
<element type="paragraph">
|
||||
foo<text bold> bar</text>
|
||||
</element>
|
||||
</editor>
|
||||
);
|
||||
expect(slateToMarkdown(slateAst.children)).toMatchInlineSnapshot(`"foo **bar**"`);
|
||||
});
|
||||
|
||||
it('should group adjacent marks into a single mark when possible', () => {
|
||||
// prettier-ignore
|
||||
const slateAst = (
|
||||
<editor>
|
||||
<element type="paragraph">
|
||||
<text bold>shared mark</text>
|
||||
<element type="link" data={{ url: "link" }}>
|
||||
<text bold><text italic>link</text></text>
|
||||
</element>
|
||||
{' '}
|
||||
<text bold>not shared mark</text>
|
||||
<element type="link" data={{ url: "link" }}>
|
||||
<text italic>another </text>
|
||||
<text bold><text italic>link</text></text>
|
||||
</element>
|
||||
</element>
|
||||
</editor>
|
||||
);
|
||||
expect(slateToMarkdown(slateAst.children)).toMatchInlineSnapshot(
|
||||
`"**shared mark*[link](link)*** **not shared mark***[another **link**](link)*"`,
|
||||
);
|
||||
});
|
||||
|
||||
describe('links', () => {
|
||||
it('should handle inline code in link content', () => {
|
||||
// prettier-ignore
|
||||
const slateAst = (
|
||||
<editor>
|
||||
<element type="paragraph">
|
||||
<element type="link" data={{ url: "link" }}>
|
||||
<text code>foo</text>
|
||||
</element>
|
||||
</element>
|
||||
</editor>
|
||||
);
|
||||
expect(slateToMarkdown(slateAst.children)).toMatchInlineSnapshot(`"[\`foo\`](link)"`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('code marks', () => {
|
||||
it('can contain other marks', () => {
|
||||
// prettier-ignore
|
||||
const slateAst = (
|
||||
<editor>
|
||||
<element type="paragraph">
|
||||
<text code><text italic><text bold>foo</text></text></text>
|
||||
</element>
|
||||
</editor>
|
||||
);
|
||||
expect(slateToMarkdown(slateAst.children)).toMatchInlineSnapshot(`"***\`foo\`***"`);
|
||||
});
|
||||
|
||||
it('can be condensed when no other marks are present', () => {
|
||||
// prettier-ignore
|
||||
const slateAst = (
|
||||
<editor>
|
||||
<element type="paragraph">
|
||||
<text code>foo</text>
|
||||
<text code>bar</text>
|
||||
</element>
|
||||
</editor>
|
||||
);
|
||||
expect(slateToMarkdown(slateAst.children)).toMatchInlineSnapshot(`"\`foo\`"`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with nested styles within a single word', () => {
|
||||
it('should not produce invalid markdown when a bold word has italics applied to a smaller part', () => {
|
||||
// prettier-ignore
|
||||
const slateAst = (
|
||||
<editor>
|
||||
<element type="paragraph">
|
||||
<text bold>h</text>
|
||||
<text bold><text italic>e</text></text>
|
||||
<text bold>y</text>
|
||||
</element>
|
||||
</editor>
|
||||
);
|
||||
expect(slateToMarkdown(slateAst.children)).toMatchInlineSnapshot(`"**h*e*y**"`);
|
||||
});
|
||||
|
||||
it('should not produce invalid markdown when an italic word has bold applied to a smaller part', () => {
|
||||
// prettier-ignore
|
||||
const slateAst = (
|
||||
<editor>
|
||||
<element type="paragraph">
|
||||
<text italic>h</text>
|
||||
<text italic><text bold>e</text></text>
|
||||
<text italic>y</text>
|
||||
</element>
|
||||
</editor>
|
||||
);
|
||||
expect(slateToMarkdown(slateAst.children)).toMatchInlineSnapshot(`"*h**e**y*"`);
|
||||
});
|
||||
|
||||
it('should handle italics inside bold inside strikethrough', () => {
|
||||
// prettier-ignore
|
||||
const slateAst = (
|
||||
<editor>
|
||||
<element type="paragraph">
|
||||
<text delete>h</text>
|
||||
<text delete><text bold>e</text></text>
|
||||
<text delete><text bold><text italic>l</text></text></text>
|
||||
<text delete><text bold>l</text></text>
|
||||
<text delete>o</text>
|
||||
</element>
|
||||
</editor>
|
||||
);
|
||||
expect(slateToMarkdown(slateAst.children)).toMatchInlineSnapshot(`"~~h**e*l*l**o~~"`);
|
||||
});
|
||||
|
||||
it('should handle bold inside italics inside strikethrough', () => {
|
||||
// prettier-ignore
|
||||
const slateAst = (
|
||||
<editor>
|
||||
<element type="paragraph">
|
||||
<text delete>h</text>
|
||||
<text delete><text italic>e</text></text>
|
||||
<text delete><text italic><text bold>l</text></text></text>
|
||||
<text delete><text italic>l</text></text>
|
||||
<text delete>o</text>
|
||||
</element>
|
||||
</editor>
|
||||
);
|
||||
expect(slateToMarkdown(slateAst.children)).toMatchInlineSnapshot(`"~~h*e**l**l*o~~"`);
|
||||
});
|
||||
|
||||
it('should handle strikethrough inside italics inside bold', () => {
|
||||
// prettier-ignore
|
||||
const slateAst = (
|
||||
<editor>
|
||||
<element type="paragraph">
|
||||
<text bold>h</text>
|
||||
<text bold><text italic>e</text></text>
|
||||
<text bold><text italic><text delete>l</text></text></text>
|
||||
<text bold><text italic>l</text></text>
|
||||
<text bold>o</text>
|
||||
</element>
|
||||
</editor>
|
||||
);
|
||||
expect(slateToMarkdown(slateAst.children)).toMatchInlineSnapshot(`"**h*e~~l~~l*o**"`);
|
||||
});
|
||||
|
||||
it('should handle italics inside strikethrough inside bold', () => {
|
||||
// prettier-ignore
|
||||
const slateAst = (
|
||||
<editor>
|
||||
<element type="paragraph">
|
||||
<text bold>h</text>
|
||||
<text bold><text delete>e</text></text>
|
||||
<text bold><text delete><text italic>l</text></text></text>
|
||||
<text bold><text delete>l</text></text>
|
||||
<text bold>o</text>
|
||||
</element>
|
||||
</editor>
|
||||
);
|
||||
expect(slateToMarkdown(slateAst.children)).toMatchInlineSnapshot(`"**h~~e*l*l~~o**"`);
|
||||
});
|
||||
|
||||
it('should handle strikethrough inside bold inside italics', () => {
|
||||
// prettier-ignore
|
||||
const slateAst = (
|
||||
<editor>
|
||||
<element type="paragraph">
|
||||
<text italic>h</text>
|
||||
<text italic><text bold>e</text></text>
|
||||
<text italic><text bold><text delete>l</text></text></text>
|
||||
<text italic><text bold>l</text></text>
|
||||
<text italic>o</text>
|
||||
</element>
|
||||
</editor>
|
||||
);
|
||||
expect(slateToMarkdown(slateAst.children)).toMatchInlineSnapshot(`"*h**e~~l~~l**o*"`);
|
||||
});
|
||||
|
||||
it('should handle bold inside strikethrough inside italics', () => {
|
||||
// prettier-ignore
|
||||
const slateAst = (
|
||||
<editor>
|
||||
<element type="paragraph">
|
||||
<text italic>h</text>
|
||||
<text italic><text delete>e</text></text>
|
||||
<text italic><text delete><text bold>l</text></text></text>
|
||||
<text italic><text delete>l</text></text>
|
||||
<text italic>o</text>
|
||||
</element>
|
||||
</editor>
|
||||
);
|
||||
expect(slateToMarkdown(slateAst.children)).toMatchInlineSnapshot(`"*h~~e**l**l~~o*"`);
|
||||
});
|
||||
});
|
||||
});
|
||||
226
node_modules/decap-cms-widget-markdown/src/serializers/index.js
generated
vendored
Normal file
226
node_modules/decap-cms-widget-markdown/src/serializers/index.js
generated
vendored
Normal file
@@ -0,0 +1,226 @@
|
||||
import { trimEnd } from 'lodash';
|
||||
import unified from 'unified';
|
||||
import u from 'unist-builder';
|
||||
import markdownToRemarkPlugin from 'remark-parse';
|
||||
import remarkToMarkdownPlugin from 'remark-stringify';
|
||||
import remarkToRehype from 'remark-rehype';
|
||||
import rehypeToHtml from 'rehype-stringify';
|
||||
import htmlToRehype from 'rehype-parse';
|
||||
import rehypeToRemark from 'rehype-remark';
|
||||
|
||||
import remarkToRehypeShortcodes from './remarkRehypeShortcodes';
|
||||
import rehypePaperEmoji from './rehypePaperEmoji';
|
||||
import remarkAssertParents from './remarkAssertParents';
|
||||
import remarkPaddedLinks from './remarkPaddedLinks';
|
||||
import remarkWrapHtml from './remarkWrapHtml';
|
||||
import remarkToSlate from './remarkSlate';
|
||||
import remarkSquashReferences from './remarkSquashReferences';
|
||||
import { remarkParseShortcodes, createRemarkShortcodeStringifier } from './remarkShortcodes';
|
||||
import remarkEscapeMarkdownEntities from './remarkEscapeMarkdownEntities';
|
||||
import remarkStripTrailingBreaks from './remarkStripTrailingBreaks';
|
||||
import remarkAllowHtmlEntities from './remarkAllowHtmlEntities';
|
||||
import slateToRemark from './slateRemark';
|
||||
import { getEditorComponents } from '../MarkdownControl';
|
||||
|
||||
/**
|
||||
* This module contains all serializers for the Markdown widget.
|
||||
*
|
||||
* The value of a Markdown widget is transformed to various formats during
|
||||
* editing, and these formats are referenced throughout serializer source
|
||||
* documentation. Below is brief glossary of the formats used.
|
||||
*
|
||||
* - Markdown {string}
|
||||
* The stringified Markdown value. The value of the field is persisted
|
||||
* (stored) in this format, and the stringified value is also used when the
|
||||
* editor is in "raw" Markdown mode.
|
||||
*
|
||||
* - MDAST {object}
|
||||
* Also loosely referred to as "Remark". MDAST stands for MarkDown AST
|
||||
* (Abstract Syntax Tree), and is an object representation of a Markdown
|
||||
* document. Underneath, it's a Unist tree with a Markdown-specific schema.
|
||||
* MDAST syntax is a part of the Unified ecosystem, and powers the Remark
|
||||
* processor, so Remark plugins may be used.
|
||||
*
|
||||
* - HAST {object}
|
||||
* Also loosely referred to as "Rehype". HAST, similar to MDAST, is an object
|
||||
* representation of an HTML document. The field value takes this format
|
||||
* temporarily before the document is stringified to HTML.
|
||||
*
|
||||
* - HTML {string}
|
||||
* The field value is stringified to HTML for preview purposes - the HTML value
|
||||
* is never parsed, it is output only.
|
||||
*
|
||||
* - Slate Raw AST {object}
|
||||
* Slate's Raw AST is a very simple and unopinionated object representation of
|
||||
* a document in a Slate editor. We define our own Markdown-specific schema
|
||||
* for serialization to/from Slate's Raw AST and MDAST.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Deserialize a Markdown string to an MDAST.
|
||||
*/
|
||||
export function markdownToRemark(markdown, remarkPlugins) {
|
||||
const processor = unified()
|
||||
.use(markdownToRemarkPlugin, { fences: true, commonmark: true })
|
||||
.use(markdownToRemarkRemoveTokenizers, { inlineTokenizers: ['url'] })
|
||||
.use(remarkParseShortcodes, { plugins: getEditorComponents() })
|
||||
.use(remarkAllowHtmlEntities)
|
||||
.use(remarkSquashReferences)
|
||||
.use(remarkPlugins);
|
||||
|
||||
/**
|
||||
* Parse the Markdown string input to an MDAST.
|
||||
*/
|
||||
const parsed = processor.parse(markdown);
|
||||
|
||||
/**
|
||||
* Further transform the MDAST with plugins.
|
||||
*/
|
||||
const result = processor.runSync(parsed);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove named tokenizers from the parser, effectively deactivating them.
|
||||
*/
|
||||
function markdownToRemarkRemoveTokenizers({ inlineTokenizers }) {
|
||||
inlineTokenizers &&
|
||||
inlineTokenizers.forEach(tokenizer => {
|
||||
delete this.Parser.prototype.inlineTokenizers[tokenizer];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize an MDAST to a Markdown string.
|
||||
*/
|
||||
export function remarkToMarkdown(obj, remarkPlugins) {
|
||||
/**
|
||||
* Rewrite the remark-stringify text visitor to simply return the text value,
|
||||
* without encoding or escaping any characters. This means we're completely
|
||||
* trusting the markdown that we receive.
|
||||
*/
|
||||
function remarkAllowAllText() {
|
||||
const Compiler = this.Compiler;
|
||||
const visitors = Compiler.prototype.visitors;
|
||||
visitors.text = node => node.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide an empty MDAST if no value is provided.
|
||||
*/
|
||||
const mdast = obj || u('root', [u('paragraph', [u('text', '')])]);
|
||||
|
||||
const remarkToMarkdownPluginOpts = {
|
||||
commonmark: true,
|
||||
fences: true,
|
||||
listItemIndent: '1',
|
||||
|
||||
/**
|
||||
* Use asterisk for everything, it's the most versatile. Eventually using
|
||||
* other characters should be an option.
|
||||
*/
|
||||
bullet: '*',
|
||||
emphasis: '*',
|
||||
strong: '*',
|
||||
rule: '-',
|
||||
};
|
||||
|
||||
const processor = unified()
|
||||
.use({ settings: remarkToMarkdownPluginOpts })
|
||||
.use(remarkEscapeMarkdownEntities)
|
||||
.use(remarkStripTrailingBreaks)
|
||||
.use(remarkToMarkdownPlugin)
|
||||
.use(remarkAllowAllText)
|
||||
.use(createRemarkShortcodeStringifier({ plugins: getEditorComponents() }))
|
||||
.use(remarkPlugins);
|
||||
|
||||
/**
|
||||
* Transform the MDAST with plugins.
|
||||
*/
|
||||
const processedMdast = processor.runSync(mdast);
|
||||
|
||||
/**
|
||||
* Serialize the MDAST to markdown.
|
||||
*/
|
||||
const markdown = processor.stringify(processedMdast).replace(/\r?/g, '');
|
||||
|
||||
/**
|
||||
* Return markdown with trailing whitespace removed.
|
||||
*/
|
||||
return trimEnd(markdown);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Markdown to HTML.
|
||||
*/
|
||||
export function markdownToHtml(markdown, { getAsset, resolveWidget, remarkPlugins = [] } = {}) {
|
||||
const mdast = markdownToRemark(markdown, remarkPlugins);
|
||||
|
||||
const hast = unified()
|
||||
.use(remarkToRehypeShortcodes, { plugins: getEditorComponents(), getAsset, resolveWidget })
|
||||
.use(remarkToRehype, { allowDangerousHTML: true })
|
||||
.runSync(mdast);
|
||||
|
||||
const html = unified()
|
||||
.use(rehypeToHtml, {
|
||||
allowDangerousHtml: true,
|
||||
allowDangerousCharacters: true,
|
||||
closeSelfClosing: true,
|
||||
entities: { useNamedReferences: true },
|
||||
})
|
||||
.stringify(hast);
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize an HTML string to Slate's Raw AST. Currently used for HTML
|
||||
* pastes.
|
||||
*/
|
||||
export function htmlToSlate(html) {
|
||||
const hast = unified().use(htmlToRehype, { fragment: true }).parse(html);
|
||||
|
||||
const mdast = unified()
|
||||
.use(rehypePaperEmoji)
|
||||
.use(rehypeToRemark, { minify: false })
|
||||
.runSync(hast);
|
||||
|
||||
const slateRaw = unified()
|
||||
.use(remarkAssertParents)
|
||||
.use(remarkPaddedLinks)
|
||||
.use(remarkWrapHtml)
|
||||
.use(remarkToSlate)
|
||||
.runSync(mdast);
|
||||
|
||||
return slateRaw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Markdown to Slate's Raw AST.
|
||||
*/
|
||||
export function markdownToSlate(markdown, { voidCodeBlock, remarkPlugins = [] } = {}) {
|
||||
const mdast = markdownToRemark(markdown, remarkPlugins);
|
||||
|
||||
const slateRaw = unified()
|
||||
.use(remarkWrapHtml)
|
||||
.use(remarkToSlate, { voidCodeBlock })
|
||||
.runSync(mdast);
|
||||
|
||||
return slateRaw.children;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Slate Raw AST to Markdown.
|
||||
*
|
||||
* Requires shortcode plugins to parse shortcode nodes back to text.
|
||||
*
|
||||
* Note that Unified is not utilized for the conversion from Slate's Raw AST to
|
||||
* MDAST. The conversion is manual because Unified can only operate on Unist
|
||||
* trees.
|
||||
*/
|
||||
export function slateToMarkdown(raw, { voidCodeBlock, remarkPlugins = [] } = {}) {
|
||||
const mdast = slateToRemark(raw, { voidCodeBlock });
|
||||
const markdown = remarkToMarkdown(mdast, remarkPlugins);
|
||||
return markdown;
|
||||
}
|
||||
16
node_modules/decap-cms-widget-markdown/src/serializers/rehypePaperEmoji.js
generated
vendored
Normal file
16
node_modules/decap-cms-widget-markdown/src/serializers/rehypePaperEmoji.js
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Dropbox Paper outputs emoji characters as images, and stores the actual
|
||||
* emoji character in a `data-emoji-ch` attribute on the image. This plugin
|
||||
* replaces the images with the emoji characters.
|
||||
*/
|
||||
export default function rehypePaperEmoji() {
|
||||
function transform(node) {
|
||||
if (node.tagName === 'img' && node.properties.dataEmojiCh) {
|
||||
return { type: 'text', value: node.properties.dataEmojiCh };
|
||||
}
|
||||
node.children = node.children ? node.children.map(transform) : node.children;
|
||||
return node;
|
||||
}
|
||||
|
||||
return transform;
|
||||
}
|
||||
58
node_modules/decap-cms-widget-markdown/src/serializers/remarkAllowHtmlEntities.js
generated
vendored
Normal file
58
node_modules/decap-cms-widget-markdown/src/serializers/remarkAllowHtmlEntities.js
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
export default function remarkAllowHtmlEntities() {
|
||||
this.Parser.prototype.inlineTokenizers.text = text;
|
||||
|
||||
/**
|
||||
* This is a port of the `remark-parse` text tokenizer, adapted to exclude
|
||||
* HTML entity decoding.
|
||||
*/
|
||||
function text(eat, value, silent) {
|
||||
var self = this;
|
||||
var methods;
|
||||
var tokenizers;
|
||||
var index;
|
||||
var length;
|
||||
var subvalue;
|
||||
var position;
|
||||
var tokenizer;
|
||||
var name;
|
||||
var min;
|
||||
|
||||
/* istanbul ignore if - never used (yet) */
|
||||
if (silent) {
|
||||
return true;
|
||||
}
|
||||
|
||||
methods = self.inlineMethods;
|
||||
length = methods.length;
|
||||
tokenizers = self.inlineTokenizers;
|
||||
index = -1;
|
||||
min = value.length;
|
||||
|
||||
while (++index < length) {
|
||||
name = methods[index];
|
||||
|
||||
if (name === 'text' || !tokenizers[name]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tokenizer = tokenizers[name].locator;
|
||||
|
||||
if (!tokenizer) {
|
||||
eat.file.fail('Missing locator: `' + name + '`');
|
||||
}
|
||||
|
||||
position = tokenizer.call(self, value, 1);
|
||||
|
||||
if (position !== -1 && position < min) {
|
||||
min = position;
|
||||
}
|
||||
}
|
||||
|
||||
subvalue = value.slice(0, min);
|
||||
|
||||
eat(subvalue)({
|
||||
type: 'text',
|
||||
value: subvalue,
|
||||
});
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user