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

draft-js

Package Overview
Dependencies
Maintainers
3
Versions
32
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

draft-js - npm Package Compare versions

Comparing version 0.2.2 to 0.3.0

lib/convertFromHTMLToContentBlocks.js

13

CHANGELOG.md

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

## 0.3.0 (March 22, 2016)
### Fixed
* Properly extract custom inline styles for `convertToRaw`
* Fix internal paste behavior to better handle copied custom blocks
### Added
* Export `getVisibleSelectionRect`
* Export `convertFromHTML`
* Export `DraftEditorBlock`
## 0.2.2 (March 9, 2016)

@@ -9,0 +22,0 @@

8

lib/Draft.js

@@ -20,2 +20,3 @@ /**

var DraftEditor = require('./DraftEditor.react');
var DraftEditorBlock = require('./DraftEditorBlock.react');
var DraftModifier = require('./DraftModifier');

@@ -30,8 +31,11 @@ var DraftEntity = require('./DraftEntity');

var convertFromDraftStateToRaw = require('./convertFromDraftStateToRaw');
var convertFromHTMLToContentBlocks = require('./convertFromHTMLToContentBlocks');
var convertFromRawToDraftState = require('./convertFromRawToDraftState');
var generateRandomKey = require('./generateRandomKey');
var getDefaultKeyBinding = require('./getDefaultKeyBinding');
var getVisibleSelectionRect = require('./getVisibleSelectionRect');
var DraftPublic = {
Editor: DraftEditor,
EditorBlock: DraftEditorBlock,
EditorState: EditorState,

@@ -53,8 +57,10 @@

convertFromHTML: convertFromHTMLToContentBlocks,
convertFromRaw: convertFromRawToDraftState,
convertToRaw: convertFromDraftStateToRaw,
genKey: generateRandomKey,
getDefaultKeyBinding: getDefaultKeyBinding
getDefaultKeyBinding: getDefaultKeyBinding,
getVisibleSelectionRect: getVisibleSelectionRect
};
module.exports = DraftPublic;

23

lib/DraftEditor.react.js

@@ -41,2 +41,3 @@ /**

var emptyFunction = require('fbjs/lib/emptyFunction');
var generateRandomKey = require('./generateRandomKey');
var getDefaultKeyBinding = require('./getDefaultKeyBinding');

@@ -85,2 +86,4 @@ var nullthrows = require('fbjs/lib/nullthrows');

function DraftEditor(props) {
var _this = this;
_classCallCheck(this, DraftEditor);

@@ -95,2 +98,3 @@

this._dragCount = 0;
this._editorKey = generateRandomKey();

@@ -128,2 +132,5 @@ this._onBeforeInput = this._buildHandler('onBeforeInput');

this.getClipboard = this._getClipboard.bind(this);
this.getEditorKey = function () {
return _this._editorKey;
};
this.update = this._update.bind(this);

@@ -146,8 +153,8 @@ this.onDragEnter = this._onDragEnter.bind(this);

value: function _buildHandler(eventName) {
var _this = this;
var _this2 = this;
return function (e) {
if (!_this.props.readOnly) {
var method = _this._handler && _this._handler[eventName];
method && method.call(_this, e);
if (!_this2.props.readOnly) {
var method = _this2._handler && _this2._handler[eventName];
method && method.call(_this2, e);
}

@@ -187,3 +194,4 @@ };

outline: 'none',
whiteSpace: 'pre-wrap'
whiteSpace: 'pre-wrap',
wordWrap: 'break-word'
};

@@ -245,2 +253,3 @@

customStyleMap: _extends({}, DefaultDraftInlineStyle, this.props.customStyleMap),
editorKey: this._editorKey,
editorState: this.props.editorState

@@ -363,6 +372,6 @@ })

value: function _restoreEditorDOM(scrollPosition) {
var _this2 = this;
var _this3 = this;
this.setState({ containerKey: this.state.containerKey + 1 }, function () {
_this2._focus(scrollPosition);
_this3._focus(scrollPosition);
});

@@ -369,0 +378,0 @@ }

@@ -174,2 +174,3 @@ /**

'data-block': true,
'data-editor': this.props.editorKey,
'data-offset-key': offsetKey,

@@ -176,0 +177,0 @@ key: key

@@ -18,377 +18,14 @@ /**

var ContentBlock = require('./ContentBlock');
var DraftEntity = require('./DraftEntity');
var Immutable = require('immutable');
var URI = require('fbjs/lib/URI');
var convertFromHTMLtoContentBlocks = require('./convertFromHTMLToContentBlocks');
var generateRandomKey = require('./generateRandomKey');
var getSafeBodyFromHTML = require('./getSafeBodyFromHTML');
var invariant = require('fbjs/lib/invariant');
var nullthrows = require('fbjs/lib/nullthrows');
var sanitizeDraftText = require('./sanitizeDraftText');
var List = Immutable.List;
var OrderedSet = Immutable.OrderedSet;
var Repeat = Immutable.Repeat;
var NBSP = ' ';
var SPACE = ' ';
// Corresponds to max indent in campfire editor
var MAX_DEPTH = 4;
// used for replacing characters in HTML
var REGEX_CR = new RegExp('\r', 'g');
var REGEX_LF = new RegExp('\n', 'g');
var REGEX_NBSP = new RegExp(NBSP, 'g');
// Block tag flow is different because LIs do not have
// a deterministic style ;_;
var blockTags = ['p', 'h1', 'h2', 'h3', 'li', 'blockquote', 'pre'];
var inlineTags = {
b: 'BOLD',
code: 'CODE',
del: 'STRIKETHROUGH',
em: 'ITALIC',
i: 'ITALIC',
s: 'STRIKETHROUGH',
strike: 'STRIKETHROUGH',
strong: 'BOLD',
u: 'UNDERLINE'
};
var lastBlock;
function getEmptyChunk() {
return {
text: '',
inlines: [],
entities: [],
blocks: []
};
}
function getWhitespaceChunk(inEntity) {
var entities = new Array(1);
if (inEntity) {
entities[0] = inEntity;
}
return {
text: SPACE,
inlines: [OrderedSet()],
entities: entities,
blocks: []
};
}
function getSoftNewlineChunk() {
return {
text: '\n',
inlines: [OrderedSet()],
entities: new Array(1),
blocks: []
};
}
function getBlockDividerChunk(block, depth) {
return {
text: '\r',
inlines: [OrderedSet()],
entities: new Array(1),
blocks: [{
type: block,
depth: Math.max(0, Math.min(MAX_DEPTH, depth))
}]
};
}
function getBlockTypeForTag(tag, lastList) {
switch (tag) {
case 'h1':
return 'header-one';
case 'h2':
return 'header-two';
case 'h3':
return 'header-three';
case 'h4':
return 'header-four';
case 'h5':
return 'header-five';
case 'h6':
return 'header-six';
case 'li':
if (lastList === 'ol') {
return 'ordered-list-item';
}
return 'unordered-list-item';
case 'blockquote':
return 'blockquote';
case 'pre':
return 'code-block';
default:
return 'unstyled';
}
}
function processInlineTag(tag, node, currentStyle) {
var styleToCheck = inlineTags[tag];
if (styleToCheck) {
currentStyle = currentStyle.add(styleToCheck).toOrderedSet();
} else if (node instanceof HTMLElement) {
(function () {
var htmlElement = node;
currentStyle = currentStyle.withMutations(function (style) {
if (htmlElement.style.fontWeight === 'bold') {
style.add('BOLD');
}
if (htmlElement.style.fontStyle === 'italic') {
style.add('ITALIC');
}
if (htmlElement.style.textDecoration === 'underline') {
style.add('UNDERLINE');
}
if (htmlElement.style.textDecoration === 'line-through') {
style.add('STRIKETHROUGH');
}
}).toOrderedSet();
})();
}
return currentStyle;
}
function joinChunks(A, B) {
// Sometimes two blocks will touch in the DOM and we need to strip the
// extra delimiter to preserve niceness.
var lastInB = B.text.slice(0, 1);
if (A.text.slice(-1) === '\r' && lastInB === '\r') {
A.text = A.text.slice(0, -1);
A.inlines.pop();
A.entities.pop();
A.blocks.pop();
}
// Kill whitespace after blocks
if (A.text.slice(-1) === '\r') {
if (B.text === SPACE || B.text === '\n') {
return A;
} else if (lastInB === SPACE || lastInB === '\n') {
B.text = B.text.slice(1);
B.inlines.shift();
B.entities.shift();
}
}
return {
text: A.text + B.text,
inlines: A.inlines.concat(B.inlines),
entities: A.entities.concat(B.entities),
blocks: A.blocks.concat(B.blocks)
};
}
/**
* Check to see if we have anything like <p> <blockquote> <h1>... to create
* block tags from. If we do, we can use those and ignore <div> tags. If we
* don't, we can treat <div> tags as meaningful (unstyled) blocks.
*/
function containsSemanticBlockMarkup(html) {
return blockTags.some(function (tag) {
return html.indexOf('<' + tag) !== -1;
});
}
function hasValidLinkText(link) {
!(link instanceof HTMLAnchorElement) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Link must be an HTMLAnchorElement.') : invariant(false) : undefined;
var protocol = link.protocol;
return protocol === 'http:' || protocol === 'https:';
}
function genFragment(node, inlineStyle, lastList, inBlock, blockTags, depth, inEntity) {
var nodeName = node.nodeName.toLowerCase();
var newBlock = false;
var nextBlockType = 'unstyled';
var lastLastBlock = lastBlock;
// Base Case
if (nodeName === '#text') {
var text = node.textContent;
if (text.trim() === '' && inBlock !== 'pre') {
return getWhitespaceChunk(inEntity);
}
if (inBlock !== 'pre') {
// Can't use empty string because MSWord
text = text.replace(REGEX_LF, SPACE);
}
// save the last block so we can use it later
lastBlock = nodeName;
return {
text: text,
inlines: Array(text.length).fill(inlineStyle),
entities: Array(text.length).fill(inEntity),
blocks: []
};
}
// save the last block so we can use it later
lastBlock = nodeName;
// BR tags
if (nodeName === 'br') {
if (lastLastBlock === 'br' && (!inBlock || getBlockTypeForTag(inBlock, lastList) === 'unstyled')) {
return getBlockDividerChunk('unstyled', depth);
}
return getSoftNewlineChunk();
}
var chunk = getEmptyChunk();
var newChunk = null;
// Inline tags
inlineStyle = processInlineTag(nodeName, node, inlineStyle);
// Handle lists
if (nodeName === 'ul' || nodeName === 'ol') {
if (lastList) {
depth += 1;
}
lastList = nodeName;
}
// Block Tags
if (!inBlock && blockTags.indexOf(nodeName) !== -1) {
chunk = getBlockDividerChunk(getBlockTypeForTag(nodeName, lastList), depth);
inBlock = nodeName;
newBlock = true;
} else if (lastList && inBlock === 'li' && nodeName === 'li') {
chunk = getBlockDividerChunk(getBlockTypeForTag(nodeName, lastList), depth);
inBlock = nodeName;
newBlock = true;
nextBlockType = lastList === 'ul' ? 'unordered-list-item' : 'ordered-list-item';
}
// Recurse through children
var child = node.firstChild;
if (child != null) {
nodeName = child.nodeName.toLowerCase();
}
var entityId = null;
var href = null;
while (child) {
if (nodeName === 'a' && child.href && hasValidLinkText(child)) {
href = new URI(child.href).toString();
entityId = DraftEntity.create('LINK', 'MUTABLE', { url: href });
} else {
entityId = undefined;
}
newChunk = genFragment(child, inlineStyle, lastList, inBlock, blockTags, depth, entityId || inEntity);
chunk = joinChunks(chunk, newChunk);
var sibling = child.nextSibling;
// Put in a newline to break up blocks inside blocks
if (sibling && blockTags.indexOf(nodeName) >= 0 && inBlock) {
chunk = joinChunks(chunk, getSoftNewlineChunk());
}
if (sibling) {
nodeName = sibling.nodeName.toLowerCase();
}
child = sibling;
}
if (newBlock) {
chunk = joinChunks(chunk, getBlockDividerChunk(nextBlockType, depth));
}
return chunk;
}
function getChunkForHTML(html) {
html = html.trim().replace(REGEX_CR, '').replace(REGEX_NBSP, SPACE);
var safeBody = getSafeBodyFromHTML(html);
if (!safeBody) {
return null;
}
lastBlock = null;
// Sometimes we aren't dealing with content that contains nice semantic
// tags. In this case, use divs to separate everything out into paragraphs
// and hope for the best.
var workingBlocks = containsSemanticBlockMarkup(html) ? blockTags : ['div'];
// Start with -1 block depth to offset the fact that we are passing in a fake
// UL block to start with.
var chunk = genFragment(safeBody, OrderedSet(), 'ul', null, workingBlocks, -1);
// join with previous block to prevent weirdness on paste
if (chunk.text.indexOf('\r') === 0) {
chunk = {
text: chunk.text.slice(1),
inlines: chunk.inlines.slice(1),
entities: chunk.entities.slice(1),
blocks: chunk.blocks
};
}
// Kill block delimiter at the end
if (chunk.text.slice(-1) === '\r') {
chunk.text = chunk.text.slice(0, -1);
chunk.inlines = chunk.inlines.slice(0, -1);
chunk.entities = chunk.entities.slice(0, -1);
chunk.blocks.pop();
}
// If we saw no block tags, put an unstyled one in
if (chunk.blocks.length === 0) {
chunk.blocks.push({ type: 'unstyled', depth: 0 });
}
// Sometimes we start with text that isn't in a block, which is then
// followed by blocks. Need to fix up the blocks to add in
// an unstyled block for this content
if (chunk.text.split('\r').length === chunk.blocks.length + 1) {
chunk.blocks.unshift({ type: 'unstyled', depth: 0 });
}
return chunk;
}
var DraftPasteProcessor = {
processHTML: function processHTML(html) {
var chunk = getChunkForHTML(html);
if (chunk == null) {
return null;
}
var start = 0;
return chunk.text.split('\r').map(function (textBlock, ii) {
// Make absolutely certain that our text is acceptable.
textBlock = sanitizeDraftText(textBlock);
var end = start + textBlock.length;
var inlines = nullthrows(chunk).inlines.slice(start, end);
var entities = nullthrows(chunk).entities.slice(start, end);
var characterList = List(inlines.map(function (style, ii) {
var data = { style: style, entity: null };
if (entities[ii]) {
data.entity = entities[ii];
}
return CharacterMetadata.create(data);
}));
start = end + 1;
return new ContentBlock({
key: generateRandomKey(),
type: nullthrows(chunk).blocks[ii].type,
depth: nullthrows(chunk).blocks[ii].depth,
text: textBlock,
characterList: characterList
});
});
return convertFromHTMLtoContentBlocks(html);
},

@@ -395,0 +32,0 @@

@@ -73,3 +73,3 @@ /**

var textBlocks = null;
var textBlocks = [];
var text = data.getText();

@@ -89,16 +89,14 @@ this.props.onPasteRawText && this.props.onPasteRawText(text);

// paste will preserve the newlines correctly.
if (data.isRichText() && this.getClipboard()) {
textBlocks = nullthrows(textBlocks);
var textBlocksWithoutNewlines = textBlocks.filter(filterOutNewlines);
var currentClipboard = this.getClipboard();
var clipboardWithoutNewlines = currentClipboard.toSeq().map(function (clipBlock) {
return clipBlock.getText();
}).filter(filterOutNewlines).toArray();
var clipboardMatch = textBlocksWithoutNewlines.every(function (line, ii) {
return line === clipboardWithoutNewlines[ii];
});
if (clipboardMatch) {
this.update(insertFragment(this.props.editorState, currentClipboard));
var html = data.getHTML();
var internalClipboard = this.getClipboard();
if (data.isRichText() && internalClipboard) {
if (
// If the editorKey is present in the pasted HTML, it should be safe to
// assume this is an internal paste.
html.indexOf(this.getEditorKey()) !== -1 ||
// The copy may have been made within a single block, in which case the
// editor key won't be part of the paste. In this case, just check
// whether the pasted text matches the internal clipboard.
textBlocks.length === 1 && internalClipboard.size === 1 && internalClipboard.first().getText() === text) {
this.update(insertFragment(this.props.editorState, internalClipboard));
return;

@@ -109,6 +107,4 @@ }

// If there is html paste data, try to parse that.
var htmlData = data.getHTML();
if (htmlData) {
var htmlFragment = DraftPasteProcessor.processHTML(htmlData);
if (html) {
var htmlFragment = DraftPasteProcessor.processHTML(html);
if (htmlFragment) {

@@ -115,0 +111,0 @@ var htmlMap = BlockMapBuilder.createFromArray(htmlFragment);

@@ -62,10 +62,9 @@ /**

}).toList();
var styles = Object.keys(DefaultDraftInlineStyle);
var ranges = styles.map(function (style) {
var ranges = styleList.flatten().toSet().map(function (style) {
return getEncodedInlinesForType(block, styleList, style);
});
return Array.prototype.concat.apply(EMPTY_ARRAY, ranges);
return Array.prototype.concat.apply(EMPTY_ARRAY, ranges.toJS());
}
module.exports = encodeInlineStyleRanges;
{
"name": "draft-js",
"description": "A React framework for building text editors.",
"version": "0.2.2",
"version": "0.3.0",
"keywords": [

@@ -23,2 +23,3 @@ "draftjs",

"scripts": {
"prepublish": "npm run build",
"pretest": "node node_modules/fbjs-scripts/node/check-dev-engines.js package.json",

@@ -31,3 +32,3 @@ "build": "gulp",

"dependencies": {
"fbjs": "^0.8.0-alpha.1",
"fbjs": "^0.8.0-alpha.2",
"immutable": "^3.7.4"

@@ -51,5 +52,7 @@ },

"gulp-browserify-thin": "^0.1.5",
"gulp-clean-css": "^2.0.3",
"gulp-concat-css": "^2.2.0",
"gulp-derequire": "^2.1.0",
"gulp-flatten": "^0.2.0",
"gulp-header": "^1.7.1",
"gulp-uglify": "^1.2.0",

@@ -56,0 +59,0 @@ "gulp-util": "^3.0.6",

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

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