1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-05-01 18:28:03 +02:00
Ian Storm Taylor 226b6592dc add linting
2016-07-06 20:19:19 -07:00

198 lines
3.6 KiB
JavaScript

import Block from '../models/block'
import Document from '../models/document'
import Inline from '../models/inline'
import Mark from '../models/mark'
import Raw from './raw'
import State from '../models/state'
import Text from '../models/text'
import cheerio from 'cheerio'
/**
* A rule to serialize text nodes.
*/
const TEXT_RULE = {
deserialize(el) {
if (el.type != 'text') return
return {
kind: 'text',
ranges: [
{
text: el.data
}
]
}
}
}
/**
* A rule to serialize <br> nodes.
*/
const BR_RULE = {
deserialize(el) {
if (el.tagName != 'br') return
return {
kind: 'text',
ranges: [
{
text: '\n'
}
]
}
}
}
/**
* HTML serializer.
*/
class Html {
/**
* Create a new serializer with `rules`.
*
* @param {Array} rules
* @return {Html} serializer
*/
constructor(rules = []) {
this.rules = [
...rules,
TEXT_RULE,
BR_RULE
]
}
/**
* Deserialize pasted HTML.
*
* @param {String} html
* @return {State} state
*/
deserialize(html) {
const $ = cheerio.load(html).root()
const children = $.children().toArray()
let nodes = this.deserializeElements(children)
// HACK: ensure for now that all top-level inline are wrapping into a block.
nodes = nodes.reduce((memo, node, i, original) => {
if (node.kind == 'block') {
memo.push(node)
return memo
}
if (i > 0 && original[i - 1].kind != 'block') {
const block = memo[memo.length - 1]
block.nodes.push(node)
return memo
}
const block = {
kind: 'block',
type: 'paragraph',
nodes: [node]
}
memo.push(block)
return memo
}, [])
const state = Raw.deserialize({ nodes })
return state
}
/**
* Deserialize an array of Cheerio `elements`.
*
* @param {Array} elements
* @return {Array} nodes
*/
deserializeElements(elements = []) {
let nodes = []
elements.forEach((element) => {
const node = this.deserializeElement(element)
if (!node) return
if (Array.isArray(node)) {
nodes = nodes.concat(node)
} else {
nodes.push(node)
}
})
return nodes
}
/**
* Deserialize a Cheerio `element`.
*
* @param {Object} element
* @return {Mixed} node
*/
deserializeElement(element) {
let node
const next = (elements) => {
return Array.isArray(elements)
? this.deserializeElements(elements)
: this.deserializeElement(elements)
}
for (const rule of this.rules) {
const ret = rule.deserialize(element, next)
if (!ret) continue
node = ret.kind == 'mark'
? this.deserializeMark(ret)
: ret
}
return node || next(element.children)
}
/**
* Deserialize a `mark` object.
*
* @param {Object} mark
* @return {Array} nodes
*/
deserializeMark(mark) {
const { type, data } = mark
const applyMark = (node) => {
if (node.kind == 'mark') return this.deserializeMark(node)
if (node.kind != 'text') {
node.nodes = node.nodes.map(applyMark)
} else {
node.ranges = node.ranges.map((range) => {
range.marks = range.marks || []
range.marks.push({ type, data })
return range
})
}
return node
}
return mark.nodes.reduce((nodes, node) => {
const ret = applyMark(node)
if (Array.isArray(ret)) return nodes.concat(ret)
nodes.push(ret)
return nodes
}, [])
}
}
/**
* Export.
*/
export default Html