Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

draftjs-filters

Package Overview
Dependencies
Maintainers
1
Versions
22
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

draftjs-filters - npm Package Compare versions

Comparing version 2.2.4 to 2.3.0

6

CHANGELOG.md

@@ -7,2 +7,8 @@ # Changelog

# [2.3.0](https://github.com/thibaudcolas/draftjs-filters/compare/v2.2.4...v2.3.0) (2020-01-03)
### Features
- **api:** add `blockTextRules` parameter to options. Fix [#65](https://github.com/thibaudcolas/draftjs-filters/issues/65) ([#127](https://github.com/thibaudcolas/draftjs-filters/issues/127)) ([2850d72](https://github.com/thibaudcolas/draftjs-filters/commit/2850d7219abe8f17411c93376c62448accd1516d))
## [2.2.4](https://github.com/thibaudcolas/draftjs-filters/compare/v2.2.3...v2.2.4) (2019-10-11)

@@ -9,0 +15,0 @@

369

dist/draftjs-filters.cjs.js

@@ -14,7 +14,5 @@ // @flow

var ORDERED_LIST_ITEM = "ordered-list-item";
var IMAGE = "IMAGE";
// @flow
/**

@@ -26,5 +24,7 @@ * Creates atomic blocks where they would be required for a block-level entity

*/
var preserveAtomicBlocks = function preserveAtomicBlocks(content /*: ContentState*/) {
var preserveAtomicBlocks = function preserveAtomicBlocks(content
/*: ContentState*/
) {
var blockMap = content.getBlockMap();
var perservedBlocks = blockMap.filter(function (block) {

@@ -34,3 +34,2 @@ var text = block.getText();

var shouldPreserve = entityKey && ["📷", " ", "📷 "].includes(text);
return shouldPreserve;

@@ -49,3 +48,2 @@ }).map(function (block) {

};
/**

@@ -55,6 +53,8 @@ * Resets atomic blocks to have a single-space char and no styles.

*/
var resetAtomicBlocks = function resetAtomicBlocks(content /*: ContentState*/) {
var resetAtomicBlocks = function resetAtomicBlocks(content
/*: ContentState*/
) {
var blockMap = content.getBlockMap();
var blocks = blockMap;
var normalisedBlocks = blocks.filter(function (block) {

@@ -66,10 +66,7 @@ return block.getType() === ATOMIC && (block.getText() !== " " || block.getInlineStyleAt(0).size !== 0);

var newChar = char;
char.getStyle().forEach(function (type) {
newChar = draftJs.CharacterMetadata.removeStyle(newChar, type);
});
return newChar;
});
return block.merge({

@@ -89,7 +86,11 @@ text: " ",

};
/**
* Removes atomic blocks for which the entity isn't whitelisted.
*/
var removeInvalidAtomicBlocks = function removeInvalidAtomicBlocks(whitelist /*: $ReadOnlyArray<{ type: string }>*/, content /*: ContentState*/) {
var removeInvalidAtomicBlocks = function removeInvalidAtomicBlocks(whitelist
/*: $ReadOnlyArray<{ type: string }>*/
, content
/*: ContentState*/
) {
var blockMap = content.getBlockMap();

@@ -103,9 +104,8 @@

var entityKey = block.getEntityAt(0);
var isValid = void 0;
var isValid;
if (entityKey) {
var _type = content.getEntity(entityKey).getType();
var type = content.getEntity(entityKey).getType();
isValid = whitelist.some(function (t) {
return t.type === _type;
return t.type === type;
});

@@ -131,3 +131,2 @@ } else {

// @flow
/**

@@ -137,3 +136,6 @@ * Removes blocks that have a non-zero depth, and aren't list items.

*/
var removeInvalidDepthBlocks = function removeInvalidDepthBlocks(content /*: ContentState*/) {
var removeInvalidDepthBlocks = function removeInvalidDepthBlocks(content
/*: ContentState*/
) {
var blockMap = content.getBlockMap();

@@ -143,3 +145,2 @@

var isListBlock = [UNORDERED_LIST_ITEM, ORDERED_LIST_ITEM].includes(block.getType());
return isListBlock || block.getDepth() === 0;

@@ -158,3 +159,2 @@ };

};
/**

@@ -167,9 +167,13 @@ * Changes block type and depth based on the block's text. – some word processors

*/
var preserveBlockByText = function preserveBlockByText(rules /*: $ReadOnlyArray<{
test: string,
type: string,
depth: number,
}>*/, content /*: ContentState*/) {
var preserveBlockByText = function preserveBlockByText(rules
/*: $ReadOnlyArray<{
test: string,
type: string,
depth: number,
}>*/
, content
/*: ContentState*/
) {
var blockMap = content.getBlockMap();
var blocks = blockMap.filter(function (block) {

@@ -180,4 +184,3 @@ return block.getType() === "unstyled";

var newBlock = block;
var match = void 0;
var match;
var matchingRule = rules.find(function (rule) {

@@ -190,11 +193,9 @@ match = new RegExp(rule.test).exec(text);

var _text = block.getText();
var entity = block.getEntityAt(0);
// Special case – do not convert the block if there is an entity at the start, and the matching text is the full block’s text.
var entity = block.getEntityAt(0); // Special case – do not convert the block if there is an entity at the start, and the matching text is the full block’s text.
// This can happen in Word for equations, which are injected as images with text "📷 ".
if (entity && match[0] === _text) {
return newBlock;
}
// Unicode gotcha:
} // Unicode gotcha:
// At the moment, Draft.js stores one CharacterMetadata in the character list

@@ -206,8 +207,10 @@ // for each "character" in an astral symbol. "📷" has a length of 2, is stored with two CharacterMetadata instances.

// See https://mathiasbynens.be/notes/javascript-unicode.
var sliceOffset = match[0].length;
// Maintain persistence in the list while removing chars from the start.
var sliceOffset = match[0].length; // Maintain persistence in the list while removing chars from the start.
// https://github.com/facebook/draft-js/blob/788595984da7c1e00d1071ea82b063ff87140be4/src/model/transaction/removeRangeFromContentState.js#L333
var chars = block.getCharacterList();
var startOffset = 0;
while (startOffset < sliceOffset) {

@@ -228,3 +231,2 @@ chars = chars.shift();

});
return blocks.size === 0 ? content : content.merge({

@@ -234,9 +236,12 @@ blockMap: blockMap.merge(blocks)

};
/**
* Resets the depth of all the content to at most max.
*/
var limitBlockDepth = function limitBlockDepth(max /*: number*/, content /*: ContentState*/) {
var limitBlockDepth = function limitBlockDepth(max
/*: number*/
, content
/*: ContentState*/
) {
var blockMap = content.getBlockMap();
var changedBlocks = blockMap.filter(function (block) {

@@ -247,3 +252,2 @@ return block.getDepth() > max;

});
return changedBlocks.size === 0 ? content : content.merge({

@@ -253,3 +257,2 @@ blockMap: blockMap.merge(changedBlocks)

};
/**

@@ -259,5 +262,9 @@ * Converts all block types not present in the whitelist to unstyled.

*/
var filterBlockTypes = function filterBlockTypes(whitelist /*: $ReadOnlyArray<string>*/, content /*: ContentState*/) {
var filterBlockTypes = function filterBlockTypes(whitelist
/*: $ReadOnlyArray<string>*/
, content
/*: ContentState*/
) {
var blockMap = content.getBlockMap();
var changedBlocks = blockMap.filter(function (block) {

@@ -271,3 +278,2 @@ return !whitelist.includes(block.getType());

});
return changedBlocks.size === 0 ? content : content.merge({

@@ -279,15 +285,16 @@ blockMap: blockMap.merge(changedBlocks)

// @flow
/**
* Removes all styles not present in the whitelist.
*/
var filterInlineStyles = function filterInlineStyles(whitelist /*: $ReadOnlyArray<string>*/, content /*: ContentState*/) {
var filterInlineStyles = function filterInlineStyles(whitelist
/*: $ReadOnlyArray<string>*/
, content
/*: ContentState*/
) {
var blockMap = content.getBlockMap();
var blocks = blockMap.map(function (block) {
var altered = false;
var chars = block.getCharacterList().map(function (char) {
var newChar = char;
char.getStyle().filter(function (type) {

@@ -299,9 +306,6 @@ return !whitelist.includes(type);

});
return newChar;
});
return altered ? block.set("characterList", chars) : block;
});
return content.merge({

@@ -313,3 +317,2 @@ blockMap: blockMap.merge(blocks)

// @flow
/**

@@ -320,9 +323,10 @@ * Clones entities in the entityMap, so each range points to its own entity instance.

*/
var cloneEntities = function cloneEntities(content /*: ContentState*/) {
var cloneEntities = function cloneEntities(content
/*: ContentState*/
) {
var newContent = content;
var blockMap = newContent.getBlockMap();
var encounteredEntities = []; // Marks ranges that need cloning, because their entity has been encountered previously.
var encounteredEntities = [];
// Marks ranges that need cloning, because their entity has been encountered previously.
var shouldCloneEntity = function shouldCloneEntity(firstChar) {

@@ -340,18 +344,15 @@ var key = firstChar.getEntity();

return false;
};
}; // We're going to update blocks that contain ranges pointing at the same entity as other ranges.
// We're going to update blocks that contain ranges pointing at the same entity as other ranges.
var blocks = blockMap.map(function (block) {
var newChars = block.getCharacterList();
var altered = false;
var altered = false; // Updates ranges for which the entity needs to be cloned.
// Updates ranges for which the entity needs to be cloned.
var updateRangeWithClone = function updateRangeWithClone(start, end) {
var key = newChars.get(start).getEntity();
var entity = newContent.getEntity(key);
newContent = newContent.createEntity(entity.getType(), entity.getMutability(), entity.getData());
var newKey = newContent.getLastCreatedEntityKey();
var newKey = newContent.getLastCreatedEntityKey(); // Update all of the chars in the range with the new entity.
// Update all of the chars in the range with the new entity.
newChars = newChars.map(function (char, i) {

@@ -364,3 +365,2 @@ if (start <= i && i <= end) {

});
altered = true;

@@ -370,6 +370,4 @@ };

block.findEntityRanges(shouldCloneEntity, updateRangeWithClone);
return altered ? block.set("characterList", newChars) : block;
});
return newContent.merge({

@@ -379,3 +377,2 @@ blockMap: blockMap.merge(blocks)

};
/*:: import type { BlockNode } from "draft-js/lib/BlockNode.js.flow" */

@@ -388,9 +385,13 @@

*/
var filterEntityRanges = function filterEntityRanges(filterFn /*: (
content: ContentState,
entityKey: string,
block: BlockNode,
) => boolean*/, content /*: ContentState*/) {
var filterEntityRanges = function filterEntityRanges(filterFn
/*: (
content: ContentState,
entityKey: string,
block: BlockNode,
) => boolean*/
, content
/*: ContentState*/
) {
var blockMap = content.getBlockMap();
/*

@@ -404,5 +405,5 @@ * Removes entities from the character list if the entity isn't enabled.

*/
var blocks = blockMap.map(function (block) {
var altered = false;
var chars = block.getCharacterList().map(function (char) {

@@ -422,6 +423,4 @@ var entityKey = char.getEntity();

});
return altered ? block.set("characterList", chars) : block;
});
return content.merge({

@@ -431,7 +430,11 @@ blockMap: blockMap.merge(blocks)

};
/**
* Keeps all entity types (images, links, documents, embeds) that are enabled.
*/
var shouldKeepEntityType = function shouldKeepEntityType(whitelist /*: $ReadOnlyArray<{ type: string }>*/, type /*: string*/) {
var shouldKeepEntityType = function shouldKeepEntityType(whitelist
/*: $ReadOnlyArray<{ type: string }>*/
, type
/*: string*/
) {
return whitelist.some(function (e) {

@@ -441,3 +444,2 @@ return e.type === type;

};
/**

@@ -447,21 +449,31 @@ * Removes invalid images – they should only be in atomic blocks.

*/
var shouldRemoveImageEntity = function shouldRemoveImageEntity(entityType /*: string*/, blockType /*: string*/) {
var shouldRemoveImageEntity = function shouldRemoveImageEntity(entityType
/*: string*/
, blockType
/*: string*/
) {
return entityType === IMAGE && blockType !== ATOMIC;
};
/**
* Filters entities based on the data they contain.
*/
var shouldKeepEntityByAttribute = function shouldKeepEntityByAttribute(entityTypes /*: $ReadOnlyArray<{
type: string,
whitelist?: {
[attribute: string]: string | boolean,
},
}>*/, entityType /*: string*/, data /*: {}*/) {
var shouldKeepEntityByAttribute = function shouldKeepEntityByAttribute(entityTypes
/*: $ReadOnlyArray<{
type: string,
whitelist?: {
[attribute: string]: string | boolean,
},
}>*/
, entityType
/*: string*/
, data
/*: {}*/
) {
var config = entityTypes.find(function (t) {
return t.type === entityType;
});
// If no whitelist is defined, the filter keeps the entity.
}); // If no whitelist is defined, the filter keeps the entity.
var whitelist = config && config.whitelist ? config.whitelist : {};
var isValid = Object.keys(whitelist).every(function (attr) {

@@ -472,3 +484,2 @@ var check = whitelist[attr];

var hasData = data.hasOwnProperty(attr);
return check ? hasData : !hasData;

@@ -479,6 +490,4 @@ }

});
return isValid;
};
/**

@@ -489,12 +498,17 @@ * Filters data on an entity to only retain what is whitelisted.

*/
var filterEntityData = function filterEntityData(entityTypes /*: $ReadOnlyArray<{
type: string,
attributes?: $ReadOnlyArray<string>,
}>*/, content /*: ContentState*/) {
var filterEntityData = function filterEntityData(entityTypes
/*: $ReadOnlyArray<{
type: string,
attributes?: $ReadOnlyArray<string>,
}>*/
, content
/*: ContentState*/
) {
var newContent = content;
var entities = {};
newContent.getBlockMap().forEach(function (block) {
block.findEntityRanges(function (char) {
var entityKey = char.getEntity();
if (entityKey) {

@@ -506,3 +520,2 @@ var entity = newContent.getEntity(entityKey);

});
Object.keys(entities).forEach(function (key) {

@@ -514,5 +527,4 @@ var entity = entities[key];

});
var whitelist = config ? config.attributes : null;
var whitelist = config ? config.attributes : null; // If no whitelist is defined, keep all of the data.
// If no whitelist is defined, keep all of the data.
if (!whitelist) {

@@ -530,6 +542,4 @@ return data;

}, {});
newContent = newContent.replaceEntityData(key, newData);
});
return newContent;

@@ -539,23 +549,23 @@ };

// @flow
/**
* Replaces the given characters by their equivalent length of spaces, in all blocks.
*/
var replaceTextBySpaces = function replaceTextBySpaces(characters /*: $ReadOnlyArray<string>*/, content /*: ContentState*/) {
var replaceTextBySpaces = function replaceTextBySpaces(characters
/*: $ReadOnlyArray<string>*/
, content
/*: ContentState*/
) {
var blockMap = content.getBlockMap();
var blocks = blockMap.map(function (block) {
var text = block.getText();
// Only replaces the character(s) with as many spaces as their length,
var text = block.getText(); // Only replaces the character(s) with as many spaces as their length,
// so that style and entity ranges are left undisturbed.
// If we want to completely remove the character, we also need to filter
// the corresponding CharacterMetadata entities.
var newText = characters.reduce(function (txt, char) {
return txt.replace(new RegExp(char, "g"), " ".repeat(char.length));
}, text);
return text !== newText ? block.set("text", newText) : block;
});
return content.merge({

@@ -567,3 +577,2 @@ blockMap: blockMap.merge(blocks)

// @flow
/**

@@ -574,9 +583,16 @@ * Applies the new content to the editor state, optionally moving the selection

*/
var applyContentWithSelection = function applyContentWithSelection(editorState /*: EditorState*/, content /*: ContentState*/, nextContent /*: ContentState*/) {
var applyContentWithSelection = function applyContentWithSelection(editorState
/*: EditorState*/
, content
/*: ContentState*/
, nextContent
/*: ContentState*/
) {
// If the content is the same before/after, return the state unaltered.
if (nextContent === content) {
return editorState;
}
} // If the block map is empty, insert a new unstyled block and put the selection on it.
// If the block map is empty, insert a new unstyled block and put the selection on it.
if (nextContent.getBlockMap().size === 0) {

@@ -593,7 +609,7 @@ return draftJs.EditorState.moveFocusToEnd(draftJs.EditorState.set(editorState, {

var anchorKey = selection.getAnchorKey();
var anchorBlock = nextContent.getBlockForKey(anchorKey);
var anchorBlock = nextContent.getBlockForKey(anchorKey); // We only support moving collapsed selections, which is the only behavior of selections after paste.
// And if the anchor block is valid, no need to move the selection.
// We only support moving collapsed selections, which is the only behavior of selections after paste.
// And if the anchor block is valid, no need to move the selection.
var shouldKeepSelection = !selection.isCollapsed() || !!anchorBlock;
if (shouldKeepSelection) {

@@ -603,11 +619,9 @@ return nextState;

var nextKeys = nextContent.getBlockMap().keySeq();
var nextKeys = nextContent.getBlockMap().keySeq(); // Find the first key whose successor is different in the old content (because a block was removed).
// Starting from the end so the selection is preserved towards the last preserved block in the filtered region.
// Find the first key whose successor is different in the old content (because a block was removed).
// Starting from the end so the selection is preserved towards the last preserved block in the filtered region.
var nextAnchorKey = nextKeys.reverse().find(function (k) {
return content.getKeyAfter(k) !== nextContent.getKeyAfter(k);
});
}); // If the selection was already misplaced before paste, we do not move it.
// If the selection was already misplaced before paste, we do not move it.
if (nextAnchorKey) {

@@ -622,3 +636,2 @@ var nextSelectedBlock = nextContent.getBlockForKey(nextAnchorKey);

});
return draftJs.EditorState.acceptSelection(nextState, nextSelection);

@@ -632,2 +645,3 @@ }

/*:: import type { EditorState as EditorStateType } from "draft-js"*/
/*:: type FilterOptions = {

@@ -656,6 +670,16 @@ // Whitelist of allowed block types. unstyled and atomic are always included.

whitespacedCharacters: Array<string>,
// Optional: Rules used to automatically convert blocks from one type to another
// based on the block’s text. Also supports setting the block depth.
// Defaults to the filters’ built-in block prefix rules.
blockTextRules?: $ReadOnlyArray<{
// A regex as a string, to match against block text, e.g. "^(◦|o |o\t)".
test: string,
// The type to convert the block to if the test regex matches.
type: string,
// The depth to set (e.g. for list items with different prefixes per depth).
depth: number,
}>,
}*/
var PREFIX_RULES = [{
var BLOCK_PREFIX_RULES = [{
// https://regexper.com/#%5E(%C2%B7%20%7C%E2%80%A2%5Ct%7C%E2%80%A2%7C%F0%9F%93%B7%20%7C%5Ct%7C%20%5Ct)

@@ -665,7 +689,13 @@ test: "^(· |•\t|•|📷 |\t| \t)",

depth: 0
},
// https://regexper.com/#%5E(%E2%97%A6%7Co%20%7Co%5Ct)
{ test: "^(◦|o |o\t)", type: "unordered-list-item", depth: 1 },
// https://regexper.com/#%5E(%C2%A7%20%7C%EF%82%A7%5Ct%7C%E2%97%BE)
{ test: "^(§ |\t|◾)", type: "unordered-list-item", depth: 2 }, {
}, // https://regexper.com/#%5E(%E2%97%A6%7Co%20%7Co%5Ct)
{
test: "^(◦|o |o\t)",
type: "unordered-list-item",
depth: 1
}, // https://regexper.com/#%5E(%C2%A7%20%7C%EF%82%A7%5Ct%7C%E2%97%BE)
{
test: "^(§ |\t|◾)",
type: "unordered-list-item",
depth: 2
}, {
// https://regexper.com/#%5E1%7B0%2C1%7D%5Cd%5C.%5B%20%5Ct%5D

@@ -690,3 +720,2 @@ test: "^1{0,1}\\d\\.[ \t]",

}];
/**

@@ -697,3 +726,8 @@ * Applies whitelist and blacklist operations to the editor content,

*/
var filterEditorState = function filterEditorState(options /*: FilterOptions*/, editorState /*: EditorStateType*/) {
var filterEditorState = function filterEditorState(options
/*: FilterOptions*/
, editorState
/*: EditorStateType*/
) {
var blocks = options.blocks,

@@ -703,3 +737,5 @@ styles = options.styles,

maxNesting = options.maxNesting,
whitespacedCharacters = options.whitespacedCharacters;
whitespacedCharacters = options.whitespacedCharacters,
_options$blockTextRul = options.blockTextRules,
blockTextRules = _options$blockTextRul === void 0 ? BLOCK_PREFIX_RULES : _options$blockTextRul;

@@ -711,51 +747,40 @@ var shouldKeepEntityRange = function shouldKeepEntityRange(content, entityKey, block) {

var blockType = block.getType();
return shouldKeepEntityType(entities, entityType) && shouldKeepEntityByAttribute(entities, entityType, entityData) && !shouldRemoveImageEntity(entityType, blockType);
};
}; // Order matters. Some filters may need the information filtered out by others.
// Order matters. Some filters may need the information filtered out by others.
var filters = [
// 1. clean up blocks.
removeInvalidDepthBlocks, preserveBlockByText.bind(null, PREFIX_RULES), limitBlockDepth.bind(null, maxNesting),
// 2. reset styles and blocks.
filterInlineStyles.bind(null, styles),
// Add block types that are always enabled in Draft.js.
filterBlockTypes.bind(null, blocks.concat([UNSTYLED, ATOMIC])),
// 4. Process atomic blocks before processing entities.
preserveAtomicBlocks, resetAtomicBlocks,
// 5. Remove entity ranges (and linked entities)
filterEntityRanges.bind(null, shouldKeepEntityRange),
// 6. Remove/filter entity-related matters.
removeInvalidAtomicBlocks.bind(null, entities), filterEntityData.bind(null, entities),
// 7. Clone entities for which it is necessary.
cloneEntities,
// 8. Finally, do text operations.
var filters = [// 1. clean up blocks.
removeInvalidDepthBlocks, preserveBlockByText.bind(null, blockTextRules), limitBlockDepth.bind(null, maxNesting), // 2. reset styles and blocks.
filterInlineStyles.bind(null, styles), // Add block types that are always enabled in Draft.js.
filterBlockTypes.bind(null, blocks.concat([UNSTYLED, ATOMIC])), // 4. Process atomic blocks before processing entities.
preserveAtomicBlocks, resetAtomicBlocks, // 5. Remove entity ranges (and linked entities)
filterEntityRanges.bind(null, shouldKeepEntityRange), // 6. Remove/filter entity-related matters.
removeInvalidAtomicBlocks.bind(null, entities), filterEntityData.bind(null, entities), // 7. Clone entities for which it is necessary.
cloneEntities, // 8. Finally, do text operations.
replaceTextBySpaces.bind(null, whitespacedCharacters)];
var content = editorState.getCurrentContent();
var nextContent = filters.reduce(function (c, filter /*: (ContentState) => ContentState*/) {
var nextContent = filters.reduce(function (c, filter
/*: (ContentState) => ContentState*/
) {
return filter(c);
}, content);
return applyContentWithSelection(editorState, content, nextContent);
};
// @flow
exports.applyContentWithSelection = applyContentWithSelection;
exports.cloneEntities = cloneEntities;
exports.filterBlockTypes = filterBlockTypes;
exports.filterEditorState = filterEditorState;
exports.filterEntityData = filterEntityData;
exports.filterEntityRanges = filterEntityRanges;
exports.filterInlineStyles = filterInlineStyles;
exports.limitBlockDepth = limitBlockDepth;
exports.preserveAtomicBlocks = preserveAtomicBlocks;
exports.resetAtomicBlocks = resetAtomicBlocks;
exports.preserveBlockByText = preserveBlockByText;
exports.removeInvalidAtomicBlocks = removeInvalidAtomicBlocks;
exports.removeInvalidDepthBlocks = removeInvalidDepthBlocks;
exports.limitBlockDepth = limitBlockDepth;
exports.preserveBlockByText = preserveBlockByText;
exports.filterBlockTypes = filterBlockTypes;
exports.filterInlineStyles = filterInlineStyles;
exports.cloneEntities = cloneEntities;
exports.filterEntityRanges = filterEntityRanges;
exports.replaceTextBySpaces = replaceTextBySpaces;
exports.resetAtomicBlocks = resetAtomicBlocks;
exports.shouldKeepEntityByAttribute = shouldKeepEntityByAttribute;
exports.shouldKeepEntityType = shouldKeepEntityType;
exports.shouldRemoveImageEntity = shouldRemoveImageEntity;
exports.shouldKeepEntityByAttribute = shouldKeepEntityByAttribute;
exports.filterEntityData = filterEntityData;
exports.replaceTextBySpaces = replaceTextBySpaces;
exports.applyContentWithSelection = applyContentWithSelection;
exports.filterEditorState = filterEditorState;
// @flow
import { CharacterMetadata, ContentState, EditorState } from 'draft-js';
import { CharacterMetadata, EditorState, ContentState } from 'draft-js';

@@ -9,7 +9,5 @@ // @flow

var ORDERED_LIST_ITEM = "ordered-list-item";
var IMAGE = "IMAGE";
// @flow
/**

@@ -21,5 +19,7 @@ * Creates atomic blocks where they would be required for a block-level entity

*/
var preserveAtomicBlocks = function preserveAtomicBlocks(content /*: ContentState*/) {
var preserveAtomicBlocks = function preserveAtomicBlocks(content
/*: ContentState*/
) {
var blockMap = content.getBlockMap();
var perservedBlocks = blockMap.filter(function (block) {

@@ -29,3 +29,2 @@ var text = block.getText();

var shouldPreserve = entityKey && ["📷", " ", "📷 "].includes(text);
return shouldPreserve;

@@ -44,3 +43,2 @@ }).map(function (block) {

};
/**

@@ -50,6 +48,8 @@ * Resets atomic blocks to have a single-space char and no styles.

*/
var resetAtomicBlocks = function resetAtomicBlocks(content /*: ContentState*/) {
var resetAtomicBlocks = function resetAtomicBlocks(content
/*: ContentState*/
) {
var blockMap = content.getBlockMap();
var blocks = blockMap;
var normalisedBlocks = blocks.filter(function (block) {

@@ -61,10 +61,7 @@ return block.getType() === ATOMIC && (block.getText() !== " " || block.getInlineStyleAt(0).size !== 0);

var newChar = char;
char.getStyle().forEach(function (type) {
newChar = CharacterMetadata.removeStyle(newChar, type);
});
return newChar;
});
return block.merge({

@@ -84,7 +81,11 @@ text: " ",

};
/**
* Removes atomic blocks for which the entity isn't whitelisted.
*/
var removeInvalidAtomicBlocks = function removeInvalidAtomicBlocks(whitelist /*: $ReadOnlyArray<{ type: string }>*/, content /*: ContentState*/) {
var removeInvalidAtomicBlocks = function removeInvalidAtomicBlocks(whitelist
/*: $ReadOnlyArray<{ type: string }>*/
, content
/*: ContentState*/
) {
var blockMap = content.getBlockMap();

@@ -98,9 +99,8 @@

var entityKey = block.getEntityAt(0);
var isValid = void 0;
var isValid;
if (entityKey) {
var _type = content.getEntity(entityKey).getType();
var type = content.getEntity(entityKey).getType();
isValid = whitelist.some(function (t) {
return t.type === _type;
return t.type === type;
});

@@ -126,3 +126,2 @@ } else {

// @flow
/**

@@ -132,3 +131,6 @@ * Removes blocks that have a non-zero depth, and aren't list items.

*/
var removeInvalidDepthBlocks = function removeInvalidDepthBlocks(content /*: ContentState*/) {
var removeInvalidDepthBlocks = function removeInvalidDepthBlocks(content
/*: ContentState*/
) {
var blockMap = content.getBlockMap();

@@ -138,3 +140,2 @@

var isListBlock = [UNORDERED_LIST_ITEM, ORDERED_LIST_ITEM].includes(block.getType());
return isListBlock || block.getDepth() === 0;

@@ -153,3 +154,2 @@ };

};
/**

@@ -162,9 +162,13 @@ * Changes block type and depth based on the block's text. – some word processors

*/
var preserveBlockByText = function preserveBlockByText(rules /*: $ReadOnlyArray<{
test: string,
type: string,
depth: number,
}>*/, content /*: ContentState*/) {
var preserveBlockByText = function preserveBlockByText(rules
/*: $ReadOnlyArray<{
test: string,
type: string,
depth: number,
}>*/
, content
/*: ContentState*/
) {
var blockMap = content.getBlockMap();
var blocks = blockMap.filter(function (block) {

@@ -175,4 +179,3 @@ return block.getType() === "unstyled";

var newBlock = block;
var match = void 0;
var match;
var matchingRule = rules.find(function (rule) {

@@ -185,11 +188,9 @@ match = new RegExp(rule.test).exec(text);

var _text = block.getText();
var entity = block.getEntityAt(0);
// Special case – do not convert the block if there is an entity at the start, and the matching text is the full block’s text.
var entity = block.getEntityAt(0); // Special case – do not convert the block if there is an entity at the start, and the matching text is the full block’s text.
// This can happen in Word for equations, which are injected as images with text "📷 ".
if (entity && match[0] === _text) {
return newBlock;
}
// Unicode gotcha:
} // Unicode gotcha:
// At the moment, Draft.js stores one CharacterMetadata in the character list

@@ -201,8 +202,10 @@ // for each "character" in an astral symbol. "📷" has a length of 2, is stored with two CharacterMetadata instances.

// See https://mathiasbynens.be/notes/javascript-unicode.
var sliceOffset = match[0].length;
// Maintain persistence in the list while removing chars from the start.
var sliceOffset = match[0].length; // Maintain persistence in the list while removing chars from the start.
// https://github.com/facebook/draft-js/blob/788595984da7c1e00d1071ea82b063ff87140be4/src/model/transaction/removeRangeFromContentState.js#L333
var chars = block.getCharacterList();
var startOffset = 0;
while (startOffset < sliceOffset) {

@@ -223,3 +226,2 @@ chars = chars.shift();

});
return blocks.size === 0 ? content : content.merge({

@@ -229,9 +231,12 @@ blockMap: blockMap.merge(blocks)

};
/**
* Resets the depth of all the content to at most max.
*/
var limitBlockDepth = function limitBlockDepth(max /*: number*/, content /*: ContentState*/) {
var limitBlockDepth = function limitBlockDepth(max
/*: number*/
, content
/*: ContentState*/
) {
var blockMap = content.getBlockMap();
var changedBlocks = blockMap.filter(function (block) {

@@ -242,3 +247,2 @@ return block.getDepth() > max;

});
return changedBlocks.size === 0 ? content : content.merge({

@@ -248,3 +252,2 @@ blockMap: blockMap.merge(changedBlocks)

};
/**

@@ -254,5 +257,9 @@ * Converts all block types not present in the whitelist to unstyled.

*/
var filterBlockTypes = function filterBlockTypes(whitelist /*: $ReadOnlyArray<string>*/, content /*: ContentState*/) {
var filterBlockTypes = function filterBlockTypes(whitelist
/*: $ReadOnlyArray<string>*/
, content
/*: ContentState*/
) {
var blockMap = content.getBlockMap();
var changedBlocks = blockMap.filter(function (block) {

@@ -266,3 +273,2 @@ return !whitelist.includes(block.getType());

});
return changedBlocks.size === 0 ? content : content.merge({

@@ -274,15 +280,16 @@ blockMap: blockMap.merge(changedBlocks)

// @flow
/**
* Removes all styles not present in the whitelist.
*/
var filterInlineStyles = function filterInlineStyles(whitelist /*: $ReadOnlyArray<string>*/, content /*: ContentState*/) {
var filterInlineStyles = function filterInlineStyles(whitelist
/*: $ReadOnlyArray<string>*/
, content
/*: ContentState*/
) {
var blockMap = content.getBlockMap();
var blocks = blockMap.map(function (block) {
var altered = false;
var chars = block.getCharacterList().map(function (char) {
var newChar = char;
char.getStyle().filter(function (type) {

@@ -294,9 +301,6 @@ return !whitelist.includes(type);

});
return newChar;
});
return altered ? block.set("characterList", chars) : block;
});
return content.merge({

@@ -308,3 +312,2 @@ blockMap: blockMap.merge(blocks)

// @flow
/**

@@ -315,9 +318,10 @@ * Clones entities in the entityMap, so each range points to its own entity instance.

*/
var cloneEntities = function cloneEntities(content /*: ContentState*/) {
var cloneEntities = function cloneEntities(content
/*: ContentState*/
) {
var newContent = content;
var blockMap = newContent.getBlockMap();
var encounteredEntities = []; // Marks ranges that need cloning, because their entity has been encountered previously.
var encounteredEntities = [];
// Marks ranges that need cloning, because their entity has been encountered previously.
var shouldCloneEntity = function shouldCloneEntity(firstChar) {

@@ -335,18 +339,15 @@ var key = firstChar.getEntity();

return false;
};
}; // We're going to update blocks that contain ranges pointing at the same entity as other ranges.
// We're going to update blocks that contain ranges pointing at the same entity as other ranges.
var blocks = blockMap.map(function (block) {
var newChars = block.getCharacterList();
var altered = false;
var altered = false; // Updates ranges for which the entity needs to be cloned.
// Updates ranges for which the entity needs to be cloned.
var updateRangeWithClone = function updateRangeWithClone(start, end) {
var key = newChars.get(start).getEntity();
var entity = newContent.getEntity(key);
newContent = newContent.createEntity(entity.getType(), entity.getMutability(), entity.getData());
var newKey = newContent.getLastCreatedEntityKey();
var newKey = newContent.getLastCreatedEntityKey(); // Update all of the chars in the range with the new entity.
// Update all of the chars in the range with the new entity.
newChars = newChars.map(function (char, i) {

@@ -359,3 +360,2 @@ if (start <= i && i <= end) {

});
altered = true;

@@ -365,6 +365,4 @@ };

block.findEntityRanges(shouldCloneEntity, updateRangeWithClone);
return altered ? block.set("characterList", newChars) : block;
});
return newContent.merge({

@@ -374,3 +372,2 @@ blockMap: blockMap.merge(blocks)

};
/*:: import type { BlockNode } from "draft-js/lib/BlockNode.js.flow" */

@@ -383,9 +380,13 @@

*/
var filterEntityRanges = function filterEntityRanges(filterFn /*: (
content: ContentState,
entityKey: string,
block: BlockNode,
) => boolean*/, content /*: ContentState*/) {
var filterEntityRanges = function filterEntityRanges(filterFn
/*: (
content: ContentState,
entityKey: string,
block: BlockNode,
) => boolean*/
, content
/*: ContentState*/
) {
var blockMap = content.getBlockMap();
/*

@@ -399,5 +400,5 @@ * Removes entities from the character list if the entity isn't enabled.

*/
var blocks = blockMap.map(function (block) {
var altered = false;
var chars = block.getCharacterList().map(function (char) {

@@ -417,6 +418,4 @@ var entityKey = char.getEntity();

});
return altered ? block.set("characterList", chars) : block;
});
return content.merge({

@@ -426,7 +425,11 @@ blockMap: blockMap.merge(blocks)

};
/**
* Keeps all entity types (images, links, documents, embeds) that are enabled.
*/
var shouldKeepEntityType = function shouldKeepEntityType(whitelist /*: $ReadOnlyArray<{ type: string }>*/, type /*: string*/) {
var shouldKeepEntityType = function shouldKeepEntityType(whitelist
/*: $ReadOnlyArray<{ type: string }>*/
, type
/*: string*/
) {
return whitelist.some(function (e) {

@@ -436,3 +439,2 @@ return e.type === type;

};
/**

@@ -442,21 +444,31 @@ * Removes invalid images – they should only be in atomic blocks.

*/
var shouldRemoveImageEntity = function shouldRemoveImageEntity(entityType /*: string*/, blockType /*: string*/) {
var shouldRemoveImageEntity = function shouldRemoveImageEntity(entityType
/*: string*/
, blockType
/*: string*/
) {
return entityType === IMAGE && blockType !== ATOMIC;
};
/**
* Filters entities based on the data they contain.
*/
var shouldKeepEntityByAttribute = function shouldKeepEntityByAttribute(entityTypes /*: $ReadOnlyArray<{
type: string,
whitelist?: {
[attribute: string]: string | boolean,
},
}>*/, entityType /*: string*/, data /*: {}*/) {
var shouldKeepEntityByAttribute = function shouldKeepEntityByAttribute(entityTypes
/*: $ReadOnlyArray<{
type: string,
whitelist?: {
[attribute: string]: string | boolean,
},
}>*/
, entityType
/*: string*/
, data
/*: {}*/
) {
var config = entityTypes.find(function (t) {
return t.type === entityType;
});
// If no whitelist is defined, the filter keeps the entity.
}); // If no whitelist is defined, the filter keeps the entity.
var whitelist = config && config.whitelist ? config.whitelist : {};
var isValid = Object.keys(whitelist).every(function (attr) {

@@ -467,3 +479,2 @@ var check = whitelist[attr];

var hasData = data.hasOwnProperty(attr);
return check ? hasData : !hasData;

@@ -474,6 +485,4 @@ }

});
return isValid;
};
/**

@@ -484,12 +493,17 @@ * Filters data on an entity to only retain what is whitelisted.

*/
var filterEntityData = function filterEntityData(entityTypes /*: $ReadOnlyArray<{
type: string,
attributes?: $ReadOnlyArray<string>,
}>*/, content /*: ContentState*/) {
var filterEntityData = function filterEntityData(entityTypes
/*: $ReadOnlyArray<{
type: string,
attributes?: $ReadOnlyArray<string>,
}>*/
, content
/*: ContentState*/
) {
var newContent = content;
var entities = {};
newContent.getBlockMap().forEach(function (block) {
block.findEntityRanges(function (char) {
var entityKey = char.getEntity();
if (entityKey) {

@@ -501,3 +515,2 @@ var entity = newContent.getEntity(entityKey);

});
Object.keys(entities).forEach(function (key) {

@@ -509,5 +522,4 @@ var entity = entities[key];

});
var whitelist = config ? config.attributes : null;
var whitelist = config ? config.attributes : null; // If no whitelist is defined, keep all of the data.
// If no whitelist is defined, keep all of the data.
if (!whitelist) {

@@ -525,6 +537,4 @@ return data;

}, {});
newContent = newContent.replaceEntityData(key, newData);
});
return newContent;

@@ -534,23 +544,23 @@ };

// @flow
/**
* Replaces the given characters by their equivalent length of spaces, in all blocks.
*/
var replaceTextBySpaces = function replaceTextBySpaces(characters /*: $ReadOnlyArray<string>*/, content /*: ContentState*/) {
var replaceTextBySpaces = function replaceTextBySpaces(characters
/*: $ReadOnlyArray<string>*/
, content
/*: ContentState*/
) {
var blockMap = content.getBlockMap();
var blocks = blockMap.map(function (block) {
var text = block.getText();
// Only replaces the character(s) with as many spaces as their length,
var text = block.getText(); // Only replaces the character(s) with as many spaces as their length,
// so that style and entity ranges are left undisturbed.
// If we want to completely remove the character, we also need to filter
// the corresponding CharacterMetadata entities.
var newText = characters.reduce(function (txt, char) {
return txt.replace(new RegExp(char, "g"), " ".repeat(char.length));
}, text);
return text !== newText ? block.set("text", newText) : block;
});
return content.merge({

@@ -562,3 +572,2 @@ blockMap: blockMap.merge(blocks)

// @flow
/**

@@ -569,9 +578,16 @@ * Applies the new content to the editor state, optionally moving the selection

*/
var applyContentWithSelection = function applyContentWithSelection(editorState /*: EditorState*/, content /*: ContentState*/, nextContent /*: ContentState*/) {
var applyContentWithSelection = function applyContentWithSelection(editorState
/*: EditorState*/
, content
/*: ContentState*/
, nextContent
/*: ContentState*/
) {
// If the content is the same before/after, return the state unaltered.
if (nextContent === content) {
return editorState;
}
} // If the block map is empty, insert a new unstyled block and put the selection on it.
// If the block map is empty, insert a new unstyled block and put the selection on it.
if (nextContent.getBlockMap().size === 0) {

@@ -588,7 +604,7 @@ return EditorState.moveFocusToEnd(EditorState.set(editorState, {

var anchorKey = selection.getAnchorKey();
var anchorBlock = nextContent.getBlockForKey(anchorKey);
var anchorBlock = nextContent.getBlockForKey(anchorKey); // We only support moving collapsed selections, which is the only behavior of selections after paste.
// And if the anchor block is valid, no need to move the selection.
// We only support moving collapsed selections, which is the only behavior of selections after paste.
// And if the anchor block is valid, no need to move the selection.
var shouldKeepSelection = !selection.isCollapsed() || !!anchorBlock;
if (shouldKeepSelection) {

@@ -598,11 +614,9 @@ return nextState;

var nextKeys = nextContent.getBlockMap().keySeq();
var nextKeys = nextContent.getBlockMap().keySeq(); // Find the first key whose successor is different in the old content (because a block was removed).
// Starting from the end so the selection is preserved towards the last preserved block in the filtered region.
// Find the first key whose successor is different in the old content (because a block was removed).
// Starting from the end so the selection is preserved towards the last preserved block in the filtered region.
var nextAnchorKey = nextKeys.reverse().find(function (k) {
return content.getKeyAfter(k) !== nextContent.getKeyAfter(k);
});
}); // If the selection was already misplaced before paste, we do not move it.
// If the selection was already misplaced before paste, we do not move it.
if (nextAnchorKey) {

@@ -617,3 +631,2 @@ var nextSelectedBlock = nextContent.getBlockForKey(nextAnchorKey);

});
return EditorState.acceptSelection(nextState, nextSelection);

@@ -627,2 +640,3 @@ }

/*:: import type { EditorState as EditorStateType } from "draft-js"*/
/*:: type FilterOptions = {

@@ -651,6 +665,16 @@ // Whitelist of allowed block types. unstyled and atomic are always included.

whitespacedCharacters: Array<string>,
// Optional: Rules used to automatically convert blocks from one type to another
// based on the block’s text. Also supports setting the block depth.
// Defaults to the filters’ built-in block prefix rules.
blockTextRules?: $ReadOnlyArray<{
// A regex as a string, to match against block text, e.g. "^(◦|o |o\t)".
test: string,
// The type to convert the block to if the test regex matches.
type: string,
// The depth to set (e.g. for list items with different prefixes per depth).
depth: number,
}>,
}*/
var PREFIX_RULES = [{
var BLOCK_PREFIX_RULES = [{
// https://regexper.com/#%5E(%C2%B7%20%7C%E2%80%A2%5Ct%7C%E2%80%A2%7C%F0%9F%93%B7%20%7C%5Ct%7C%20%5Ct)

@@ -660,7 +684,13 @@ test: "^(· |•\t|•|📷 |\t| \t)",

depth: 0
},
// https://regexper.com/#%5E(%E2%97%A6%7Co%20%7Co%5Ct)
{ test: "^(◦|o |o\t)", type: "unordered-list-item", depth: 1 },
// https://regexper.com/#%5E(%C2%A7%20%7C%EF%82%A7%5Ct%7C%E2%97%BE)
{ test: "^(§ |\t|◾)", type: "unordered-list-item", depth: 2 }, {
}, // https://regexper.com/#%5E(%E2%97%A6%7Co%20%7Co%5Ct)
{
test: "^(◦|o |o\t)",
type: "unordered-list-item",
depth: 1
}, // https://regexper.com/#%5E(%C2%A7%20%7C%EF%82%A7%5Ct%7C%E2%97%BE)
{
test: "^(§ |\t|◾)",
type: "unordered-list-item",
depth: 2
}, {
// https://regexper.com/#%5E1%7B0%2C1%7D%5Cd%5C.%5B%20%5Ct%5D

@@ -685,3 +715,2 @@ test: "^1{0,1}\\d\\.[ \t]",

}];
/**

@@ -692,3 +721,8 @@ * Applies whitelist and blacklist operations to the editor content,

*/
var filterEditorState = function filterEditorState(options /*: FilterOptions*/, editorState /*: EditorStateType*/) {
var filterEditorState = function filterEditorState(options
/*: FilterOptions*/
, editorState
/*: EditorStateType*/
) {
var blocks = options.blocks,

@@ -698,3 +732,5 @@ styles = options.styles,

maxNesting = options.maxNesting,
whitespacedCharacters = options.whitespacedCharacters;
whitespacedCharacters = options.whitespacedCharacters,
_options$blockTextRul = options.blockTextRules,
blockTextRules = _options$blockTextRul === void 0 ? BLOCK_PREFIX_RULES : _options$blockTextRul;

@@ -706,35 +742,24 @@ var shouldKeepEntityRange = function shouldKeepEntityRange(content, entityKey, block) {

var blockType = block.getType();
return shouldKeepEntityType(entities, entityType) && shouldKeepEntityByAttribute(entities, entityType, entityData) && !shouldRemoveImageEntity(entityType, blockType);
};
}; // Order matters. Some filters may need the information filtered out by others.
// Order matters. Some filters may need the information filtered out by others.
var filters = [
// 1. clean up blocks.
removeInvalidDepthBlocks, preserveBlockByText.bind(null, PREFIX_RULES), limitBlockDepth.bind(null, maxNesting),
// 2. reset styles and blocks.
filterInlineStyles.bind(null, styles),
// Add block types that are always enabled in Draft.js.
filterBlockTypes.bind(null, blocks.concat([UNSTYLED, ATOMIC])),
// 4. Process atomic blocks before processing entities.
preserveAtomicBlocks, resetAtomicBlocks,
// 5. Remove entity ranges (and linked entities)
filterEntityRanges.bind(null, shouldKeepEntityRange),
// 6. Remove/filter entity-related matters.
removeInvalidAtomicBlocks.bind(null, entities), filterEntityData.bind(null, entities),
// 7. Clone entities for which it is necessary.
cloneEntities,
// 8. Finally, do text operations.
var filters = [// 1. clean up blocks.
removeInvalidDepthBlocks, preserveBlockByText.bind(null, blockTextRules), limitBlockDepth.bind(null, maxNesting), // 2. reset styles and blocks.
filterInlineStyles.bind(null, styles), // Add block types that are always enabled in Draft.js.
filterBlockTypes.bind(null, blocks.concat([UNSTYLED, ATOMIC])), // 4. Process atomic blocks before processing entities.
preserveAtomicBlocks, resetAtomicBlocks, // 5. Remove entity ranges (and linked entities)
filterEntityRanges.bind(null, shouldKeepEntityRange), // 6. Remove/filter entity-related matters.
removeInvalidAtomicBlocks.bind(null, entities), filterEntityData.bind(null, entities), // 7. Clone entities for which it is necessary.
cloneEntities, // 8. Finally, do text operations.
replaceTextBySpaces.bind(null, whitespacedCharacters)];
var content = editorState.getCurrentContent();
var nextContent = filters.reduce(function (c, filter /*: (ContentState) => ContentState*/) {
var nextContent = filters.reduce(function (c, filter
/*: (ContentState) => ContentState*/
) {
return filter(c);
}, content);
return applyContentWithSelection(editorState, content, nextContent);
};
// @flow
export { preserveAtomicBlocks, resetAtomicBlocks, removeInvalidAtomicBlocks, removeInvalidDepthBlocks, limitBlockDepth, preserveBlockByText, filterBlockTypes, filterInlineStyles, cloneEntities, filterEntityRanges, shouldKeepEntityType, shouldRemoveImageEntity, shouldKeepEntityByAttribute, filterEntityData, replaceTextBySpaces, applyContentWithSelection, filterEditorState };
export { applyContentWithSelection, cloneEntities, filterBlockTypes, filterEditorState, filterEntityData, filterEntityRanges, filterInlineStyles, limitBlockDepth, preserveAtomicBlocks, preserveBlockByText, removeInvalidAtomicBlocks, removeInvalidDepthBlocks, replaceTextBySpaces, resetAtomicBlocks, shouldKeepEntityByAttribute, shouldKeepEntityType, shouldRemoveImageEntity };
{
"name": "draftjs-filters",
"version": "2.2.4",
"version": "2.3.0",
"description": "Filter Draft.js content to preserve only the formatting you allow",

@@ -30,36 +30,31 @@ "author": "Thibaud Colas",

"browserslist": "> 1%, IE 11",
"babel": {
"presets": [
"env"
]
},
"devDependencies": {
"@babel/plugin-transform-flow-comments": "7.7.4",
"@commitlint/cli": "8.2.0",
"@commitlint/config-conventional": "8.2.0",
"@commitlint/travis-cli": "8.2.0",
"@semantic-release/changelog": "3.0.4",
"@semantic-release/exec": "3.3.7",
"@semantic-release/git": "7.0.16",
"babel-plugin-transform-flow-comments": "6.22.0",
"core-js": "^2.5.7",
"coveralls": "3.0.6",
"danger": "9.2.1",
"documentation": "8.1.2",
"@semantic-release/changelog": "3.0.6",
"@semantic-release/exec": "3.3.8",
"@semantic-release/git": "7.0.18",
"coveralls": "3.0.9",
"danger": "9.2.9",
"documentation": "12.1.4",
"draft-js": "0.10.5",
"enzyme": "3.10.0",
"enzyme-adapter-react-16": "1.14.0",
"enzyme-to-json": "3.4.2",
"flow-bin": "0.91.0",
"draft-js-11": "npm:draft-js@0.11.3",
"enzyme": "3.11.0",
"enzyme-adapter-react-16": "1.15.2",
"enzyme-to-json": "3.4.3",
"flow-bin": "0.114.0",
"immutable": "~3.7.6",
"normalize.css": "^7.0.0",
"prettier": "1.18.2",
"react": "16.10.2",
"react-dom": "16.10.2",
"react-scripts": "1.1.5",
"react-test-renderer": "16.10.2",
"rollup": "0.66.2",
"rollup-plugin-babel": "3.0.7",
"semantic-release": "15.13.24",
"snapshot-diff": "0.4.0",
"source-map-explorer": "2.1.0"
"normalize.css": "7.0.0",
"prettier": "1.19.1",
"react": "16.12.0",
"react-dom": "16.12.0",
"react-scripts": "3.3.0",
"react-test-renderer": "16.12.0",
"rollup": "1.27.14",
"rollup-plugin-babel": "4.3.3",
"semantic-release": "15.14.0",
"snapshot-diff": "0.6.1",
"source-map-explorer": "2.1.2"
},

@@ -72,3 +67,3 @@ "peerDependencies": {

"build": "CI=true react-scripts build && source-map-explorer --html build/static/js/main.* > build/source-map-explorer.html && rollup -c",
"test": "react-scripts test --env=jsdom --coverage",
"test": "CI=true react-scripts test --env=jsdom --coverage",
"test:watch": "react-scripts test --env=jsdom",

@@ -75,0 +70,0 @@ "report:coverage": "open coverage/lcov-report/index.html",

@@ -63,3 +63,3 @@ # [Draft.js filters](https://thibaudcolas.github.io/draftjs-filters/) [<img src="https://raw.githubusercontent.com/thibaudcolas/draftail.org/master/.github/draftail-logo.svg?sanitize=true" width="90" height="90" align="right">](https://www.draftail.org/)

Here are the available options:
Here are all the available options:

@@ -89,2 +89,13 @@ ```jsx

whitespacedCharacters: Array<string>,
// Optional: Rules used to automatically convert blocks from one type to another
// based on the block’s text. Also supports setting the block depth.
// Defaults to the filters’ built-in block prefix rules.
blockTextRules?: $ReadOnlyArray<{
// A regex as a string, to match against block text, e.g. "^(◦|o |o\t)".
test: string,
// The type to convert the block to if the test regex matches.
type: string,
// The depth to set (e.g. for list items with different prefixes per depth).
depth: number,
}>,
```

@@ -91,0 +102,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc