redraft
Version:
Renders the result of Draft.js convertToRaw using provided callbacks, works well with React
197 lines (166 loc) • 7.44 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _punycode = require('punycode');
var _punycode2 = _interopRequireDefault(_punycode);
var _ContentNode = require('./ContentNode');
var _ContentNode2 = _interopRequireDefault(_ContentNode);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
/**
* Slices the decoded ucs2 array and encodes the result back to a string representation
*/
var getString = function getString(array, from, to) {
return _punycode2.default.ucs2.encode(array.slice(from, to));
};
/**
* creates nodes with entity keys and the endOffset
*/
function createNodes(entityRanges) {
var decoratorRanges = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
var textArray = arguments[2];
var block = arguments[3];
var lastIndex = 0;
var mergedRanges = [].concat(_toConsumableArray(entityRanges), _toConsumableArray(decoratorRanges)).sort(function (a, b) {
return a.offset - b.offset;
});
var nodes = [];
// if thers no entities will return just a single item
if (mergedRanges.length < 1) {
nodes.push(new _ContentNode2.default({ block: block, start: 0, end: textArray.length }));
return nodes;
}
mergedRanges.forEach(function (range) {
// create an empty node for content between previous and this entity
if (range.offset > lastIndex) {
nodes.push(new _ContentNode2.default({ block: block, start: lastIndex, end: range.offset }));
}
// push the node for the entity
nodes.push(new _ContentNode2.default({
block: block,
entity: range.key,
decorator: range.component,
decoratorProps: range.decoratorProps,
decoratedText: range.component ? getString(textArray, range.offset, range.offset + range.length) : undefined,
start: range.offset,
end: range.offset + range.length,
contentState: range.contentState
}));
lastIndex = range.offset + range.length;
});
// finaly add a node for the remaining text if any
if (lastIndex < textArray.length) {
nodes.push(new _ContentNode2.default({
block: block,
start: lastIndex,
end: textArray.length
}));
}
return nodes;
}
function addIndexes(indexes, ranges) {
ranges.forEach(function (range) {
indexes.push(range.offset);
indexes.push(range.offset + range.length);
});
return indexes;
}
/**
* Creates an array of sorted char indexes to avoid iterating over every single character
*/
function getRelevantIndexes(text, inlineRanges) {
var entityRanges = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
var decoratorRanges = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : [];
var relevantIndexes = [];
// set indexes to corresponding keys to ensure uniquenes
relevantIndexes = addIndexes(relevantIndexes, inlineRanges);
relevantIndexes = addIndexes(relevantIndexes, entityRanges);
relevantIndexes = addIndexes(relevantIndexes, decoratorRanges);
// add text start and end to relevant indexes
relevantIndexes.push(0);
relevantIndexes.push(text.length);
var uniqueRelevantIndexes = relevantIndexes.filter(function (value, index, self) {
return self.indexOf(value) === index;
});
// and sort it
return uniqueRelevantIndexes.sort(function (aa, bb) {
return aa - bb;
});
}
var RawParser = function () {
function RawParser(_ref) {
var _ref$flat = _ref.flat,
flat = _ref$flat === undefined ? false : _ref$flat;
_classCallCheck(this, RawParser);
this.flat = flat;
}
_createClass(RawParser, [{
key: 'relevantStyles',
value: function relevantStyles(offset) {
var styles = this.ranges.filter(function (range) {
return offset >= range.offset && offset < range.offset + range.length;
});
return styles.map(function (style) {
return style.style;
});
}
/**
* Loops over relevant text indexes
*/
}, {
key: 'nodeIterator',
value: function nodeIterator(node, start, end) {
var _this = this;
var indexes = this.relevantIndexes.slice(this.relevantIndexes.indexOf(start), this.relevantIndexes.indexOf(end));
// loops while next index is smaller than the endOffset
indexes.forEach(function (index, key) {
// figure out what styles this char and the next char need
// (regardless of whether there *is* a next char or not)
var characterStyles = _this.relevantStyles(index);
// calculate distance or set it to 1 if thers no next index
var distance = indexes[key + 1] ? indexes[key + 1] - index : 1;
// add all the chars up to next relevantIndex
var text = getString(_this.textArray, index, index + distance);
node.pushContent(text, characterStyles, _this.flat);
// if thers no next index and thers more text left to push
if (!indexes[key + 1] && index < end) {
node.pushContent(getString(_this.textArray, index + 1, end), _this.relevantStyles(end - 1), _this.flat);
}
});
return node;
}
/**
* Converts raw block to object with nested style objects,
* while it returns an object not a string
* the idea is still mostly same as backdraft.js (https://github.com/evanc/backdraft-js)
*/
}, {
key: 'parse',
value: function parse(block) {
var _this2 = this;
var text = block.text,
ranges = block.inlineStyleRanges,
entityRanges = block.entityRanges,
_block$decoratorRange = block.decoratorRanges,
decoratorRanges = _block$decoratorRange === undefined ? [] : _block$decoratorRange;
// Some unicode charactes actualy have length of more than 1
// this creates an array of code points using es6 string iterator
this.textArray = _punycode2.default.ucs2.decode(text);
this.ranges = ranges;
this.iterator = 0;
// get all the relevant indexes for whole block
this.relevantIndexes = getRelevantIndexes(text, ranges, entityRanges, decoratorRanges);
// create entity or empty nodes to place the inline styles in
var nodes = createNodes(entityRanges, decoratorRanges, this.textArray, block);
var parsedNodes = nodes.map(function (node) {
return _this2.nodeIterator(node, node.start, node.end);
});
return new _ContentNode2.default({ block: block, content: parsedNodes });
}
}]);
return RawParser;
}();
exports.default = RawParser;