Serializer.ts 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. /*
  2. * <<
  3. * Davinci
  4. * ==
  5. * Copyright (C) 2016 - 2017 EDP
  6. * ==
  7. * Licensed under the Apache License, Version 2.0 (the "License");
  8. * you may not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS IS" BASIS,
  15. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. * See the License for the specific language governing permissions and
  17. * limitations under the License.
  18. * >>
  19. */
  20. import escapeHtml from 'escape-html'
  21. import { Node, Text } from 'slate'
  22. import { jsx } from 'slate-hyperscript'
  23. import { ElementTags, TextStyles, TextTags } from './Element'
  24. import { TextProperties, ElementTypes } from './Element/constants'
  25. const serializeNode = (node: Node) => {
  26. if (Text.isText(node)) {
  27. let text = escapeHtml(node.text)
  28. if (node[TextStyles.Bold]) {
  29. text = `<strong>${text}</strong>`
  30. }
  31. if (node[TextStyles.Italic]) {
  32. text = `<em>${text}</em>`
  33. }
  34. if (node[TextStyles.Underline]) {
  35. text = `<u>${text}</u>`
  36. }
  37. if (node[TextStyles.StrikeThrough]) {
  38. text = `<s>${text}</s>`
  39. }
  40. if (node[TextStyles.Code]) {
  41. text = `<code>${text}</code>`
  42. }
  43. TextProperties
  44. var span = document.createElement('span')
  45. if (node[TextProperties.BackgroundColor]) {
  46. span.style.backgroundColor = node[TextProperties.BackgroundColor]
  47. }
  48. if (node[TextProperties.Color]) {
  49. span.style.color = node[TextProperties.Color]
  50. }
  51. if (node[TextProperties.FontFamily]) {
  52. span.style.fontFamily = node[TextProperties.FontFamily]
  53. }
  54. if (node[TextProperties.FontSize]) {
  55. span.style.fontSize = `${node[TextProperties.FontSize]}px`
  56. }
  57. span.innerHTML = text
  58. text = span.outerHTML
  59. return text
  60. }
  61. const children = node.children.map((n) => serializeNode(n)).join('')
  62. switch (node.type) {
  63. case ElementTypes.Paragraph:
  64. return `<p>${children}</p>`
  65. case ElementTypes.BlockQuote:
  66. return `<blockquote>${children}</blockquote>`
  67. case ElementTypes.Code:
  68. return `<pre><code>${children}</code></pre>`
  69. case ElementTypes.BulletedList:
  70. return `<ul>${children}</ul>`
  71. case ElementTypes.NumberedList:
  72. return `<ol>${children}</ol>`
  73. case ElementTypes.ListItem:
  74. return `<li>${children}</li>`
  75. case ElementTypes.HeadingOne:
  76. return `<h1>${children}</h1>`
  77. case ElementTypes.HeadingTwo:
  78. return `<h2>${children}</h2>`
  79. case ElementTypes.HeadingThree:
  80. return `<h3>${children}</h3>`
  81. case ElementTypes.HeadingFour:
  82. return `<h4>${children}</h4>`
  83. case ElementTypes.HeadingFive:
  84. return `<h5>${children}</h5>`
  85. case ElementTypes.HeadingSix:
  86. return `<h6>${children}</h6>`
  87. case ElementTypes.Link:
  88. return `<a href="${node.url}" target="_blank">${children}</a>`
  89. case ElementTypes.Image:
  90. return `<div>${children}<img src="${node.url}" ${
  91. node.width ? `width="${node.width}"` : ''
  92. }/></div>`
  93. case ElementTypes.Table:
  94. return `<table><tbody>${children}</tbody></table>`
  95. case ElementTypes.TableRow:
  96. return `<tr>${children}</tr>`
  97. case ElementTypes.TableCell:
  98. return `<td>${children}</td>`
  99. case ElementTypes.Marquee:
  100. throw new Error(
  101. 'Marquee Element can not be serialized as simple html tag!'
  102. )
  103. default:
  104. return children
  105. }
  106. }
  107. export const serialize = (nodes: Node[]) => {
  108. const html = nodes.map((n) => serializeNode(n)).join('')
  109. return html
  110. }
  111. export const deserialize = (el: HTMLElement): Node[] => {
  112. if (el.nodeType === 3) {
  113. const textElement = [{ text: el.textContent }]
  114. if (el.parentElement.nodeName === 'BODY') {
  115. return [jsx('element', ElementTags['P'](null), textElement)]
  116. }
  117. return textElement
  118. }
  119. if (el.nodeType !== 1) {
  120. return null
  121. }
  122. if (el.nodeName === 'BR') {
  123. return [{ text: '\n' }]
  124. }
  125. const { nodeName, childNodes } = el
  126. let parent: HTMLElement | ChildNode = el
  127. if (
  128. nodeName === 'PRE' &&
  129. childNodes[0] &&
  130. childNodes[0].nodeName === 'CODE'
  131. ) {
  132. parent = el.childNodes[0]
  133. }
  134. const children: any[] = Array.from(parent.childNodes).map(deserialize).flat()
  135. if (el.nodeName === 'BODY') {
  136. return jsx('fragment', {}, children)
  137. }
  138. if (ElementTags[nodeName]) {
  139. const attrs = ElementTags[nodeName](el)
  140. ALIGN_CLASS_SUFFIXES.some((suffix) => {
  141. if (el.classList.contains(`${ALIGN_CLASS_PREFIX}${suffix}`)) {
  142. attrs.textAlign = suffix
  143. return true
  144. }
  145. })
  146. return [jsx('element', attrs, children.length ? children : [{ text: '' }])]
  147. }
  148. if (TextTags[nodeName]) {
  149. const attrs = TextTags[nodeName]()
  150. if (el.style) {
  151. const { fontSize, color, backgroundColor } = el.style
  152. if (fontSize) {
  153. attrs.fontSize = +fontSize.substring(0, fontSize.length - 2)
  154. }
  155. if (color) {
  156. attrs.color = color
  157. }
  158. if (backgroundColor) {
  159. attrs.backgroundColor = backgroundColor
  160. }
  161. }
  162. return children.map((child) => jsx('text', attrs, child))
  163. }
  164. return children
  165. }
  166. const ALIGN_CLASS_PREFIX = 'ql-align-'
  167. const ALIGN_CLASS_SUFFIXES = ['left', 'center', 'right']