react-native-enriched-html 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (272) hide show
  1. package/android/src/main/java/com/swmansion/enriched/common/EnrichedSpanFlags.kt +39 -0
  2. package/android/src/main/java/com/swmansion/enriched/common/parser/EnrichedParser.java +43 -20
  3. package/android/src/main/java/com/swmansion/enriched/common/parser/EnrichedSpanFactory.kt +1 -0
  4. package/android/src/main/java/com/swmansion/enriched/text/EnrichedTextSpanFactory.kt +1 -0
  5. package/android/src/main/java/com/swmansion/enriched/text/EnrichedTextView.kt +2 -1
  6. package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputSpannableFactory.kt +2 -1
  7. package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputView.kt +8 -5
  8. package/android/src/main/java/com/swmansion/enriched/textinput/events/MentionHandler.kt +12 -2
  9. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnMentionEvent.kt +6 -0
  10. package/android/src/main/java/com/swmansion/enriched/textinput/styles/AlignmentStyles.kt +6 -7
  11. package/android/src/main/java/com/swmansion/enriched/textinput/styles/InlineStyles.kt +13 -1
  12. package/android/src/main/java/com/swmansion/enriched/textinput/styles/ListStyles.kt +4 -3
  13. package/android/src/main/java/com/swmansion/enriched/textinput/styles/ParagraphStyles.kt +4 -3
  14. package/android/src/main/java/com/swmansion/enriched/textinput/styles/ParametrizedStyles.kt +38 -11
  15. package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedSelection.kt +2 -0
  16. package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedSpannable.kt +2 -1
  17. package/android/src/main/java/com/swmansion/enriched/textinput/utils/Utils.kt +2 -1
  18. package/cpp/parser/GumboNormalizer.c +61 -3
  19. package/cpp/tests/GumboParserTest.cpp +54 -0
  20. package/ios/EnrichedTextInputView.mm +37 -25
  21. package/ios/extensions/LayoutManagerExtension.mm +73 -62
  22. package/ios/extensions/StringExtension.mm +75 -0
  23. package/ios/htmlParser/HtmlParser.h +4 -1
  24. package/ios/htmlParser/HtmlParser.mm +23 -5
  25. package/ios/inputHtmlParser/InputHtmlParser.mm +6 -3
  26. package/ios/interfaces/StyleHeaders.h +4 -0
  27. package/ios/styles/LinkStyle.mm +20 -34
  28. package/ios/styles/MentionStyle.mm +55 -4
  29. package/ios/textHtmlParser/TextHtmlParser.mm +2 -1
  30. package/ios/utils/ParagraphAttributesUtils.mm +7 -0
  31. package/lib/module/index.js +1 -0
  32. package/lib/module/index.js.map +1 -1
  33. package/lib/module/utils/expandMentionStylesForIndicators.js +2 -3
  34. package/lib/module/utils/expandMentionStylesForIndicators.js.map +1 -1
  35. package/lib/module/utils/normalizeHtmlStyle.js +1 -1
  36. package/lib/module/utils/normalizeHtmlStyle.js.map +1 -1
  37. package/lib/module/web/EnrichedText.css +343 -0
  38. package/lib/module/web/EnrichedText.js +55 -0
  39. package/lib/module/web/EnrichedText.js.map +1 -0
  40. package/lib/module/web/EnrichedTextInput.js +54 -52
  41. package/lib/module/web/EnrichedTextInput.js.map +1 -1
  42. package/lib/module/web/constants/brokenImageGlyph.js +9 -0
  43. package/lib/module/web/constants/brokenImageGlyph.js.map +1 -0
  44. package/lib/module/web/constants/classNames.js +5 -0
  45. package/lib/module/web/constants/classNames.js.map +1 -0
  46. package/lib/module/web/emitLinkDetected.js +12 -0
  47. package/lib/module/web/emitLinkDetected.js.map +1 -0
  48. package/lib/module/web/formats/EnrichedBlockquote.js +1 -1
  49. package/lib/module/web/formats/EnrichedBlockquote.js.map +1 -1
  50. package/lib/module/web/formats/EnrichedCheckboxList.js +2 -2
  51. package/lib/module/web/formats/EnrichedCheckboxList.js.map +1 -1
  52. package/lib/module/web/formats/EnrichedCodeBlock.js +1 -1
  53. package/lib/module/web/formats/EnrichedCodeBlock.js.map +1 -1
  54. package/lib/module/web/formats/EnrichedHeading.js +1 -1
  55. package/lib/module/web/formats/EnrichedHeading.js.map +1 -1
  56. package/lib/module/web/formats/EnrichedImageNodeView.js +1 -1
  57. package/lib/module/web/formats/EnrichedImageNodeView.js.map +1 -1
  58. package/lib/module/web/formats/EnrichedLink.js +43 -3
  59. package/lib/module/web/formats/EnrichedLink.js.map +1 -1
  60. package/lib/module/web/formats/EnrichedOrderedList.js +2 -2
  61. package/lib/module/web/formats/EnrichedOrderedList.js.map +1 -1
  62. package/lib/module/web/formats/EnrichedTextAlign.js +82 -0
  63. package/lib/module/web/formats/EnrichedTextAlign.js.map +1 -0
  64. package/lib/module/web/formats/EnrichedUnorderedList.js +2 -2
  65. package/lib/module/web/formats/EnrichedUnorderedList.js.map +1 -1
  66. package/lib/module/web/formats/applyWrappingListToSelection.js +3 -2
  67. package/lib/module/web/formats/applyWrappingListToSelection.js.map +1 -1
  68. package/lib/module/web/formats/formatRules.js +39 -3
  69. package/lib/module/web/formats/formatRules.js.map +1 -1
  70. package/lib/module/web/formats/listKeyboard.js +4 -1
  71. package/lib/module/web/formats/listKeyboard.js.map +1 -1
  72. package/lib/module/web/normalization/checkboxHtmlNormalizer.js.map +1 -0
  73. package/lib/module/web/normalization/htmlNormalizer.js +514 -0
  74. package/lib/module/web/normalization/htmlNormalizer.js.map +1 -0
  75. package/lib/module/web/normalization/prepareHtmlForWeb.js +78 -0
  76. package/lib/module/web/normalization/prepareHtmlForWeb.js.map +1 -0
  77. package/lib/module/web/{tiptapHtmlNormalizer.js → normalization/tiptapHtmlNormalizer.js} +6 -2
  78. package/lib/module/web/normalization/tiptapHtmlNormalizer.js.map +1 -0
  79. package/lib/module/web/pmPlugins/AutolinkPlugin/autolinkRegex.js +27 -0
  80. package/lib/module/web/pmPlugins/AutolinkPlugin/autolinkRegex.js.map +1 -0
  81. package/lib/module/web/pmPlugins/AutolinkPlugin/index.js +178 -0
  82. package/lib/module/web/pmPlugins/AutolinkPlugin/index.js.map +1 -0
  83. package/lib/module/web/pmPlugins/MentionPlugin/index.js +1 -1
  84. package/lib/module/web/pmPlugins/MentionPlugin/index.js.map +1 -1
  85. package/lib/module/web/pmPlugins/MentionPlugin/makeMentionPluginState.js +23 -4
  86. package/lib/module/web/pmPlugins/MentionPlugin/makeMentionPluginState.js.map +1 -1
  87. package/lib/module/web/pmPlugins/MentionPlugin/setMention.js +17 -18
  88. package/lib/module/web/pmPlugins/MentionPlugin/setMention.js.map +1 -1
  89. package/lib/module/web/pmPlugins/MentionPlugin/useMentionEvents.js +105 -0
  90. package/lib/module/web/pmPlugins/MentionPlugin/useMentionEvents.js.map +1 -0
  91. package/lib/module/web/pmPlugins/ShortcutPlugin.js +11 -0
  92. package/lib/module/web/pmPlugins/ShortcutPlugin.js.map +1 -1
  93. package/lib/module/web/pmPlugins/StrictMarksPlugin.js +2 -0
  94. package/lib/module/web/pmPlugins/StrictMarksPlugin.js.map +1 -1
  95. package/lib/module/web/pmPlugins/StripBoldInStyledHeadingsPlugin.js.map +1 -1
  96. package/lib/module/web/sanitization/htmlSanitizer.js +10 -0
  97. package/lib/module/web/sanitization/htmlSanitizer.js.map +1 -0
  98. package/lib/module/web/styleConversion/buildMentionRulesCSS.js +12 -6
  99. package/lib/module/web/styleConversion/buildMentionRulesCSS.js.map +1 -1
  100. package/lib/module/web/styleConversion/enrichedBaseStyleToCSSProperties.js +159 -0
  101. package/lib/module/web/styleConversion/enrichedBaseStyleToCSSProperties.js.map +1 -0
  102. package/lib/module/web/styleConversion/enrichedInputStyleToCSSProperties.js +3 -155
  103. package/lib/module/web/styleConversion/enrichedInputStyleToCSSProperties.js.map +1 -1
  104. package/lib/module/web/styleConversion/enrichedTextStyleToCSSProperties.js +44 -0
  105. package/lib/module/web/styleConversion/enrichedTextStyleToCSSProperties.js.map +1 -0
  106. package/lib/module/web/styleConversion/{enrichedInputThemingToCSSProperties.js → enrichedThemingToCSSProperties.js} +3 -3
  107. package/lib/module/web/styleConversion/enrichedThemingToCSSProperties.js.map +1 -0
  108. package/lib/module/web/styleConversion/htmlStyleToCSSVariables.js +87 -64
  109. package/lib/module/web/styleConversion/htmlStyleToCSSVariables.js.map +1 -1
  110. package/lib/module/web/styleConversion/inlineImageCSSVariables.js +7 -0
  111. package/lib/module/web/styleConversion/inlineImageCSSVariables.js.map +1 -0
  112. package/lib/module/web/useImageErrorFallback.js +33 -0
  113. package/lib/module/web/useImageErrorFallback.js.map +1 -0
  114. package/lib/module/web/useOnChangeHtml.js +1 -1
  115. package/lib/module/web/useOnChangeHtml.js.map +1 -1
  116. package/lib/module/web/useOnChangeState.js +6 -3
  117. package/lib/module/web/useOnChangeState.js.map +1 -1
  118. package/lib/module/web/useOnLinkDetected.js +32 -45
  119. package/lib/module/web/useOnLinkDetected.js.map +1 -1
  120. package/lib/module/web/usePressInteractions.js +19 -0
  121. package/lib/module/web/usePressInteractions.js.map +1 -0
  122. package/lib/module/web/useStableRef.js +13 -0
  123. package/lib/module/web/useStableRef.js.map +1 -0
  124. package/lib/typescript/src/index.d.ts +1 -0
  125. package/lib/typescript/src/index.d.ts.map +1 -1
  126. package/lib/typescript/src/native/EnrichedText.d.ts +1 -1
  127. package/lib/typescript/src/native/EnrichedText.d.ts.map +1 -1
  128. package/lib/typescript/src/native/EnrichedTextInput.d.ts +1 -1
  129. package/lib/typescript/src/native/EnrichedTextInput.d.ts.map +1 -1
  130. package/lib/typescript/src/types.d.ts +1 -1
  131. package/lib/typescript/src/types.d.ts.map +1 -1
  132. package/lib/typescript/src/utils/expandMentionStylesForIndicators.d.ts +1 -1
  133. package/lib/typescript/src/utils/expandMentionStylesForIndicators.d.ts.map +1 -1
  134. package/lib/typescript/src/utils/normalizeHtmlStyle.d.ts.map +1 -1
  135. package/lib/typescript/src/web/EnrichedText.d.ts +4 -0
  136. package/lib/typescript/src/web/EnrichedText.d.ts.map +1 -0
  137. package/lib/typescript/src/web/EnrichedTextInput.d.ts +2 -2
  138. package/lib/typescript/src/web/EnrichedTextInput.d.ts.map +1 -1
  139. package/lib/typescript/src/web/constants/brokenImageGlyph.d.ts +3 -0
  140. package/lib/typescript/src/web/constants/brokenImageGlyph.d.ts.map +1 -0
  141. package/lib/typescript/src/web/constants/classNames.d.ts +3 -0
  142. package/lib/typescript/src/web/constants/classNames.d.ts.map +1 -0
  143. package/lib/typescript/src/web/emitLinkDetected.d.ts +10 -0
  144. package/lib/typescript/src/web/emitLinkDetected.d.ts.map +1 -0
  145. package/lib/typescript/src/web/formats/EnrichedBlockquote.d.ts.map +1 -1
  146. package/lib/typescript/src/web/formats/EnrichedCheckboxList.d.ts.map +1 -1
  147. package/lib/typescript/src/web/formats/EnrichedCodeBlock.d.ts.map +1 -1
  148. package/lib/typescript/src/web/formats/EnrichedHeading.d.ts.map +1 -1
  149. package/lib/typescript/src/web/formats/EnrichedImageNodeView.d.ts +1 -1
  150. package/lib/typescript/src/web/formats/EnrichedImageNodeView.d.ts.map +1 -1
  151. package/lib/typescript/src/web/formats/EnrichedLink.d.ts +4 -1
  152. package/lib/typescript/src/web/formats/EnrichedLink.d.ts.map +1 -1
  153. package/lib/typescript/src/web/formats/EnrichedOrderedList.d.ts.map +1 -1
  154. package/lib/typescript/src/web/formats/EnrichedTextAlign.d.ts +2 -0
  155. package/lib/typescript/src/web/formats/EnrichedTextAlign.d.ts.map +1 -0
  156. package/lib/typescript/src/web/formats/EnrichedUnorderedList.d.ts.map +1 -1
  157. package/lib/typescript/src/web/formats/applyWrappingListToSelection.d.ts.map +1 -1
  158. package/lib/typescript/src/web/formats/formatRules.d.ts +3 -1
  159. package/lib/typescript/src/web/formats/formatRules.d.ts.map +1 -1
  160. package/lib/typescript/src/web/formats/listKeyboard.d.ts.map +1 -1
  161. package/lib/typescript/src/web/normalization/checkboxHtmlNormalizer.d.ts.map +1 -0
  162. package/lib/typescript/src/web/normalization/htmlNormalizer.d.ts +2 -0
  163. package/lib/typescript/src/web/normalization/htmlNormalizer.d.ts.map +1 -0
  164. package/lib/typescript/src/web/normalization/prepareHtmlForWeb.d.ts +2 -0
  165. package/lib/typescript/src/web/normalization/prepareHtmlForWeb.d.ts.map +1 -0
  166. package/lib/typescript/src/web/normalization/tiptapHtmlNormalizer.d.ts +3 -0
  167. package/lib/typescript/src/web/normalization/tiptapHtmlNormalizer.d.ts.map +1 -0
  168. package/lib/typescript/src/web/pmPlugins/AutolinkPlugin/autolinkRegex.d.ts +7 -0
  169. package/lib/typescript/src/web/pmPlugins/AutolinkPlugin/autolinkRegex.d.ts.map +1 -0
  170. package/lib/typescript/src/web/pmPlugins/AutolinkPlugin/index.d.ts +8 -0
  171. package/lib/typescript/src/web/pmPlugins/AutolinkPlugin/index.d.ts.map +1 -0
  172. package/lib/typescript/src/web/pmPlugins/MentionPlugin/index.d.ts +1 -1
  173. package/lib/typescript/src/web/pmPlugins/MentionPlugin/index.d.ts.map +1 -1
  174. package/lib/typescript/src/web/pmPlugins/MentionPlugin/makeMentionPluginState.d.ts.map +1 -1
  175. package/lib/typescript/src/web/pmPlugins/MentionPlugin/setMention.d.ts.map +1 -1
  176. package/lib/typescript/src/web/pmPlugins/MentionPlugin/useMentionEvents.d.ts +4 -0
  177. package/lib/typescript/src/web/pmPlugins/MentionPlugin/useMentionEvents.d.ts.map +1 -0
  178. package/lib/typescript/src/web/pmPlugins/ShortcutPlugin.d.ts.map +1 -1
  179. package/lib/typescript/src/web/pmPlugins/StrictMarksPlugin.d.ts.map +1 -1
  180. package/lib/typescript/src/web/pmPlugins/StripBoldInStyledHeadingsPlugin.d.ts +6 -1
  181. package/lib/typescript/src/web/pmPlugins/StripBoldInStyledHeadingsPlugin.d.ts.map +1 -1
  182. package/lib/typescript/src/web/sanitization/htmlSanitizer.d.ts +2 -0
  183. package/lib/typescript/src/web/sanitization/htmlSanitizer.d.ts.map +1 -0
  184. package/lib/typescript/src/web/styleConversion/buildMentionRulesCSS.d.ts +2 -2
  185. package/lib/typescript/src/web/styleConversion/buildMentionRulesCSS.d.ts.map +1 -1
  186. package/lib/typescript/src/web/styleConversion/enrichedBaseStyleToCSSProperties.d.ts +8 -0
  187. package/lib/typescript/src/web/styleConversion/enrichedBaseStyleToCSSProperties.d.ts.map +1 -0
  188. package/lib/typescript/src/web/styleConversion/enrichedInputStyleToCSSProperties.d.ts +2 -5
  189. package/lib/typescript/src/web/styleConversion/enrichedInputStyleToCSSProperties.d.ts.map +1 -1
  190. package/lib/typescript/src/web/styleConversion/enrichedTextStyleToCSSProperties.d.ts +4 -0
  191. package/lib/typescript/src/web/styleConversion/enrichedTextStyleToCSSProperties.d.ts.map +1 -0
  192. package/lib/typescript/src/web/styleConversion/{enrichedInputThemingToCSSProperties.d.ts → enrichedThemingToCSSProperties.d.ts} +1 -1
  193. package/lib/typescript/src/web/styleConversion/enrichedThemingToCSSProperties.d.ts.map +1 -0
  194. package/lib/typescript/src/web/styleConversion/htmlStyleToCSSVariables.d.ts +5 -4
  195. package/lib/typescript/src/web/styleConversion/htmlStyleToCSSVariables.d.ts.map +1 -1
  196. package/lib/typescript/src/web/styleConversion/inlineImageCSSVariables.d.ts +3 -0
  197. package/lib/typescript/src/web/styleConversion/inlineImageCSSVariables.d.ts.map +1 -0
  198. package/lib/typescript/src/web/useImageErrorFallback.d.ts +3 -0
  199. package/lib/typescript/src/web/useImageErrorFallback.d.ts.map +1 -0
  200. package/lib/typescript/src/web/useOnChangeState.d.ts.map +1 -1
  201. package/lib/typescript/src/web/useOnLinkDetected.d.ts +2 -2
  202. package/lib/typescript/src/web/useOnLinkDetected.d.ts.map +1 -1
  203. package/lib/typescript/src/web/usePressInteractions.d.ts +2 -0
  204. package/lib/typescript/src/web/usePressInteractions.d.ts.map +1 -0
  205. package/lib/typescript/src/web/useStableRef.d.ts +3 -0
  206. package/lib/typescript/src/web/useStableRef.d.ts.map +1 -0
  207. package/package.json +54 -28
  208. package/src/index.tsx +1 -0
  209. package/src/types.ts +5 -3
  210. package/src/utils/expandMentionStylesForIndicators.ts +3 -3
  211. package/src/utils/normalizeHtmlStyle.ts +2 -1
  212. package/src/web/EnrichedText.css +343 -0
  213. package/src/web/EnrichedText.tsx +82 -0
  214. package/src/web/EnrichedTextInput.tsx +69 -55
  215. package/src/web/constants/brokenImageGlyph.ts +7 -0
  216. package/src/web/constants/classNames.ts +2 -0
  217. package/src/web/emitLinkDetected.ts +29 -0
  218. package/src/web/formats/EnrichedBlockquote.ts +2 -1
  219. package/src/web/formats/EnrichedCheckboxList.ts +5 -2
  220. package/src/web/formats/EnrichedCodeBlock.ts +2 -1
  221. package/src/web/formats/EnrichedHeading.ts +2 -1
  222. package/src/web/formats/EnrichedImageNodeView.tsx +1 -3
  223. package/src/web/formats/EnrichedLink.ts +59 -4
  224. package/src/web/formats/EnrichedOrderedList.ts +5 -2
  225. package/src/web/formats/EnrichedTextAlign.ts +88 -0
  226. package/src/web/formats/EnrichedUnorderedList.ts +5 -2
  227. package/src/web/formats/applyWrappingListToSelection.ts +4 -4
  228. package/src/web/formats/formatRules.ts +48 -4
  229. package/src/web/formats/listKeyboard.ts +6 -1
  230. package/src/web/normalization/htmlNormalizer.ts +656 -0
  231. package/src/web/normalization/prepareHtmlForWeb.ts +93 -0
  232. package/src/web/{tiptapHtmlNormalizer.ts → normalization/tiptapHtmlNormalizer.ts} +12 -2
  233. package/src/web/pmPlugins/AutolinkPlugin/autolinkRegex.ts +43 -0
  234. package/src/web/pmPlugins/AutolinkPlugin/index.ts +274 -0
  235. package/src/web/pmPlugins/MentionPlugin/index.ts +1 -1
  236. package/src/web/pmPlugins/MentionPlugin/makeMentionPluginState.ts +32 -5
  237. package/src/web/pmPlugins/MentionPlugin/setMention.ts +19 -29
  238. package/src/web/pmPlugins/MentionPlugin/useMentionEvents.ts +114 -0
  239. package/src/web/pmPlugins/ShortcutPlugin.ts +7 -0
  240. package/src/web/pmPlugins/StrictMarksPlugin.ts +3 -0
  241. package/src/web/pmPlugins/StripBoldInStyledHeadingsPlugin.ts +41 -36
  242. package/src/web/sanitization/htmlSanitizer.ts +8 -0
  243. package/src/web/styleConversion/buildMentionRulesCSS.ts +30 -11
  244. package/src/web/styleConversion/enrichedBaseStyleToCSSProperties.ts +228 -0
  245. package/src/web/styleConversion/enrichedInputStyleToCSSProperties.ts +6 -217
  246. package/src/web/styleConversion/enrichedTextStyleToCSSProperties.ts +53 -0
  247. package/src/web/styleConversion/{enrichedInputThemingToCSSProperties.ts → enrichedThemingToCSSProperties.ts} +4 -4
  248. package/src/web/styleConversion/htmlStyleToCSSVariables.ts +121 -70
  249. package/src/web/styleConversion/inlineImageCSSVariables.ts +6 -0
  250. package/src/web/useImageErrorFallback.ts +37 -0
  251. package/src/web/useOnChangeHtml.ts +1 -1
  252. package/src/web/useOnChangeState.ts +7 -5
  253. package/src/web/useOnLinkDetected.ts +38 -50
  254. package/src/web/usePressInteractions.tsx +22 -0
  255. package/src/web/useStableRef.ts +12 -0
  256. package/lib/module/web/EnrichedTextInput.css +0 -201
  257. package/lib/module/web/checkboxHtmlNormalizer.js.map +0 -1
  258. package/lib/module/web/pmPlugins/MentionPlugin/subscribeMentionEvents.js +0 -100
  259. package/lib/module/web/pmPlugins/MentionPlugin/subscribeMentionEvents.js.map +0 -1
  260. package/lib/module/web/styleConversion/enrichedInputThemingToCSSProperties.js.map +0 -1
  261. package/lib/module/web/tiptapHtmlNormalizer.js.map +0 -1
  262. package/lib/typescript/src/web/checkboxHtmlNormalizer.d.ts.map +0 -1
  263. package/lib/typescript/src/web/pmPlugins/MentionPlugin/subscribeMentionEvents.d.ts +0 -4
  264. package/lib/typescript/src/web/pmPlugins/MentionPlugin/subscribeMentionEvents.d.ts.map +0 -1
  265. package/lib/typescript/src/web/styleConversion/enrichedInputThemingToCSSProperties.d.ts.map +0 -1
  266. package/lib/typescript/src/web/tiptapHtmlNormalizer.d.ts +0 -3
  267. package/lib/typescript/src/web/tiptapHtmlNormalizer.d.ts.map +0 -1
  268. package/src/web/EnrichedTextInput.css +0 -201
  269. package/src/web/pmPlugins/MentionPlugin/subscribeMentionEvents.ts +0 -105
  270. /package/lib/module/web/{checkboxHtmlNormalizer.js → normalization/checkboxHtmlNormalizer.js} +0 -0
  271. /package/lib/typescript/src/web/{checkboxHtmlNormalizer.d.ts → normalization/checkboxHtmlNormalizer.d.ts} +0 -0
  272. /package/src/web/{checkboxHtmlNormalizer.ts → normalization/checkboxHtmlNormalizer.ts} +0 -0
@@ -411,6 +411,34 @@ static void emit_one_attr(buffer_t *out, GumboElement *el,
411
411
  }
412
412
  }
413
413
 
414
+ static bool is_checkbox_list(GumboElement *el) {
415
+ const char *val = get_attr(el, "data-type");
416
+ if (val && (strcmp(val, "checkbox") == 0 || strcmp(val, "checkboxList") == 0)) {
417
+ return true;
418
+ }
419
+
420
+ // In Google Docs and MS Word the <li> elements define if it is a checkbox
421
+ // list. We only need to check the first <li>.
422
+ GumboVector *children = &el->children;
423
+ for (unsigned int i = 0; i < children->length; i++) {
424
+ GumboNode *child = children->data[i];
425
+ if (is_element(child)) {
426
+ char child_tag[64];
427
+ if (get_tag_name(child, child_tag, sizeof(child_tag)) && strcmp(child_tag, "li") == 0) {
428
+ GumboElement *child_el = &child->v.element;
429
+ const char *role = get_attr(child_el, "role");
430
+ const char *cls = get_attr(child_el, "class");
431
+
432
+ // Matches Google Docs (role="checkbox") OR MS Word (class includes "checklist")
433
+ return (role && strcmp(role, "checkbox") == 0) ||
434
+ (cls && strstr(cls, "checklist") != NULL);
435
+ }
436
+ }
437
+ }
438
+
439
+ return false;
440
+ }
441
+
414
442
  static void emit_attributes(GumboElement *el, const char *tag_name,
415
443
  buffer_t *out) {
416
444
  if (strcmp(tag_name, "a") == 0) {
@@ -421,12 +449,21 @@ static void emit_attributes(GumboElement *el, const char *tag_name,
421
449
  emit_one_attr(out, el, "width");
422
450
  emit_one_attr(out, el, "height");
423
451
  } else if (strcmp(tag_name, "ul") == 0) {
424
- const char *val = get_attr(el, "data-type");
425
- if (val && strcmp(val, "checkbox") == 0)
452
+ if (is_checkbox_list(el)) {
426
453
  buffer_append_str(out, " data-type=\"checkbox\"");
454
+ }
427
455
  } else if (strcmp(tag_name, "li") == 0) {
428
- if (gumbo_get_attribute(&el->attributes, "checked") != NULL)
456
+ const char *data_checked = get_attr(el, "data-checked");
457
+ const char *aria_checked = get_attr(el, "aria-checked");
458
+ const char *level_text = get_attr(el, "data-leveltext");
459
+
460
+ // "\xEF\x83\xBE" is the UTF-8 hex encoding for U+F0FE (MS Word Checked Box)
461
+ if (gumbo_get_attribute(&el->attributes, "checked") != NULL ||
462
+ (data_checked && strcmp(data_checked, "true") == 0) ||
463
+ (aria_checked && strcmp(aria_checked, "true") == 0) ||
464
+ (level_text && strcmp(level_text, "\xEF\x83\xBE") == 0)) {
429
465
  buffer_append_str(out, " checked");
466
+ }
430
467
  } else if (strcmp(tag_name, "mention") == 0) {
431
468
  emit_one_attr(out, el, "id");
432
469
  emit_one_attr(out, el, "text");
@@ -511,6 +548,7 @@ typedef struct {
511
548
  GumboNode **nested_lists;
512
549
  int *nested_count;
513
550
  int max_nested;
551
+ bool has_emitted;
514
552
  } li_ctx_t;
515
553
 
516
554
  static void flatten_li_node(GumboNode *node, buffer_t *ib, buffer_t *out,
@@ -527,6 +565,7 @@ static void flush_li_buffer(buffer_t *ib, buffer_t *out, li_ctx_t *ctx) {
527
565
  emit_styles_close(out, ctx->styles);
528
566
  buffer_append_str(out, "</li>");
529
567
  buffer_clear(ib);
568
+ ctx->has_emitted = true;
530
569
  }
531
570
 
532
571
  static void flatten_li_children(GumboNode *node, buffer_t *ib, buffer_t *out,
@@ -551,6 +590,17 @@ static void flatten_li_node(GumboNode *node, buffer_t *ib, buffer_t *out,
551
590
  flatten_li_children(node, ib, out, ctx);
552
591
  return;
553
592
  }
593
+
594
+ char buf[64];
595
+ const char *tag = get_tag_name(node, buf, sizeof(buf));
596
+ if (tag && strcmp(tag, "img") == 0) {
597
+ const char *role = get_attr(ctx->el, "role");
598
+ // strip the <img> that Google Docs uses for the display of a checkbox icon
599
+ if (role && strcmp(role, "checkbox") == 0) {
600
+ return;
601
+ }
602
+ }
603
+
554
604
  if (is_list_node(node)) {
555
605
  if (*ctx->nested_count < ctx->max_nested) {
556
606
  ctx->nested_lists[*ctx->nested_count] = node;
@@ -837,6 +887,14 @@ static void walk_node(GumboNode *node, buffer_t *out) {
837
887
  li_ctx_t ctx = {el, es, nested_lists, &nested_count, 16};
838
888
  flatten_li_children(node, &li_ib, out, &ctx);
839
889
  flush_li_buffer(&li_ib, out, &ctx);
890
+
891
+ /* if nothing emitted - the <li> is empty, we add it manually */
892
+ if (!ctx.has_emitted) {
893
+ buffer_append_str(out, "<li");
894
+ emit_attributes(el, "li", out);
895
+ buffer_append_str(out, "></li>");
896
+ }
897
+
840
898
  free(li_ib.data);
841
899
  for (int k = 0; k < nested_count; k++)
842
900
  walk_children(nested_lists[k], out);
@@ -448,6 +448,60 @@ TEST(GumboParserTest, ListFlattening) {
448
448
  "<ul><li><b>another one </b>hi kacper,</li><li>hi</li></ul>");
449
449
  }
450
450
 
451
+ TEST(GumboParserTest, TiptapCheckboxList) {
452
+ EXPECT_EQ(
453
+ GumboParser::normalizeHtml(
454
+ "<ul data-type=\"checkboxList\"><li data-checked=\"true\" "
455
+ "data-type=\"checkboxItem\"><label><input type=\"checkbox\" "
456
+ "checked=\"checked\"><span></span></label><div><p>first</p></div></"
457
+ "li><li data-checked=\"false\" data-type=\"checkboxItem\"><label>"
458
+ "<input type=\"checkbox\"><span></span></label><div><p>second</p></"
459
+ "div></li></ul>"),
460
+ "<ul data-type=\"checkbox\"><li checked>first</li><li>second</li></ul>");
461
+ }
462
+
463
+ TEST(GumboParserTest, GoogleDocsCheckboxList) {
464
+ EXPECT_EQ(GumboParser::normalizeHtml(
465
+ "<ul><li role=\"checkbox\" aria-checked=\"true\"><img "
466
+ "src=\"data:...\" /><p>Checked</p></li><li role=\"checkbox\" "
467
+ "aria-checked=\"false\"><img src=\"data:...\" "
468
+ "/><p>Unchecked</p></li></ul>"),
469
+ "<ul data-type=\"checkbox\"><li "
470
+ "checked>Checked</li><li>Unchecked</li></ul>");
471
+ }
472
+
473
+ TEST(GumboParserTest, MSWordCheckboxList) {
474
+ // \xEF\x83\xBE is the UTF-8 hex for U+F0FE (Checked MS Word box)
475
+ // \xEF\x82\xA8 is the UTF-8 hex for U+F0A8 (Unchecked MS Word box)
476
+ EXPECT_EQ(
477
+ GumboParser::normalizeHtml(
478
+ "<ul><li class=\"OutlineElement checklist\" "
479
+ "data-leveltext=\"\xEF\x83\xBE\">Checked</li><li "
480
+ "class=\"OutlineElement "
481
+ "checklist\" data-leveltext=\"\xEF\x82\xA8\">Unchecked</li></ul>"),
482
+ "<ul data-type=\"checkbox\"><li "
483
+ "checked>Checked</li><li>Unchecked</li></ul>");
484
+ }
485
+
486
+ TEST(GumboParserTest, EmptyListItems) {
487
+ EXPECT_EQ(GumboParser::normalizeHtml("<ul><li></li><li>first</li><li></"
488
+ "li><li>second</li><li></li><li></li>"
489
+ "</ul>"),
490
+ "<ul><li></li><li>first</li><li></li><li>second</li><li></li><li></"
491
+ "li></ul>");
492
+ EXPECT_EQ(GumboParser::normalizeHtml("<ol><li></li><li>first</li><li></"
493
+ "li><li>second</li><li></li><li></li>"
494
+ "</ol>"),
495
+ "<ol><li></li><li>first</li><li></li><li>second</li><li></li><li></"
496
+ "li></ol>");
497
+ EXPECT_EQ(
498
+ GumboParser::normalizeHtml(
499
+ "<ul data-type=\"checkbox\"><li checked></li><li>first</li><li>"
500
+ "</li><li checked>second</li><li></li><li></li></ul>"),
501
+ "<ul data-type=\"checkbox\"><li checked></li><li>first</li><li></li><li "
502
+ "checked>second</li><li></li><li></li></ul>");
503
+ }
504
+
451
505
  TEST(GumboParserTest, BrRemappings) {
452
506
  EXPECT_EQ(GumboParser::normalizeHtml(
453
507
  "<p><b>Asdasdasd</b></p><br><br><p>Sent with<span> </span><a "
@@ -719,6 +719,15 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
719
719
  textShortcuts = shortcuts;
720
720
  }
721
721
 
722
+ // linkRegex
723
+ LinkRegexConfig *oldRegexConfig =
724
+ [[LinkRegexConfig alloc] initWithLinkRegexProp:oldViewProps.linkRegex];
725
+ LinkRegexConfig *newRegexConfig =
726
+ [[LinkRegexConfig alloc] initWithLinkRegexProp:newViewProps.linkRegex];
727
+ if (![newRegexConfig isEqualToConfig:oldRegexConfig]) {
728
+ [config setLinkRegexConfig:newRegexConfig];
729
+ }
730
+
722
731
  // default value - must be set before placeholder to make sure it correctly
723
732
  // shows on first mount
724
733
  if (newViewProps.defaultValue != oldViewProps.defaultValue) {
@@ -775,15 +784,6 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
775
784
  [config setMentionIndicators:newIndicators];
776
785
  }
777
786
 
778
- // linkRegex
779
- LinkRegexConfig *oldRegexConfig =
780
- [[LinkRegexConfig alloc] initWithLinkRegexProp:oldViewProps.linkRegex];
781
- LinkRegexConfig *newRegexConfig =
782
- [[LinkRegexConfig alloc] initWithLinkRegexProp:newViewProps.linkRegex];
783
- if (![newRegexConfig isEqualToConfig:oldRegexConfig]) {
784
- [config setLinkRegexConfig:newRegexConfig];
785
- }
786
-
787
787
  // selection color sets both selection and cursor on iOS (just as in RN)
788
788
  if (newViewProps.selectionColor != oldViewProps.selectionColor) {
789
789
  if (isColorMeaningful(newViewProps.selectionColor)) {
@@ -1662,8 +1662,9 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
1662
1662
  }
1663
1663
 
1664
1664
  - (void)anyTextMayHaveBeenModified {
1665
- // we don't do no text changes when working with iOS marked text
1665
+ // we don't do text changes when working with iOS marked text
1666
1666
  if (textView.markedTextRange != nullptr) {
1667
+ [self handlePlaceholderVisibility];
1667
1668
  return;
1668
1669
  }
1669
1670
 
@@ -1685,13 +1686,7 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
1685
1686
  [mentionStyleClass manageMentionEditing];
1686
1687
  }
1687
1688
 
1688
- // placholder management
1689
- if (!_placeholderLabel.hidden && textView.textStorage.string.length > 0) {
1690
- [self setPlaceholderLabelShown:NO];
1691
- } else if (textView.textStorage.string.length == 0 &&
1692
- _placeholderLabel.hidden) {
1693
- [self setPlaceholderLabelShown:YES];
1694
- }
1689
+ [self handlePlaceholderVisibility];
1695
1690
 
1696
1691
  // modified words handling
1697
1692
  NSArray *currentDirtyRanges = [attributesManager getDirtyRanges];
@@ -1722,18 +1717,23 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
1722
1717
  }
1723
1718
 
1724
1719
  if (![textView.textStorage.string isEqualToString:_recentInputString]) {
1725
- // emit onChangeText event
1726
1720
  auto emitter = [self getEventEmitter];
1727
- if (emitter != nullptr && _emitTextChange) {
1721
+ if (emitter != nullptr) {
1728
1722
  // set the recent input string only if the emitter is defined
1723
+ // to properly emit the initial onChangeText event
1724
+ // as anyTextMayHaveBeenModified also runs before the emitter's
1725
+ // initialization
1729
1726
  _recentInputString = [textView.textStorage.string copy];
1730
1727
 
1731
- // emit string without zero width spaces
1732
- NSString *stringToBeEmitted = [[textView.textStorage.string
1733
- stringByReplacingOccurrencesOfString:@"\u200B"
1734
- withString:@""] copy];
1728
+ // emit onChangeText event
1729
+ if (_emitTextChange) {
1730
+ // emit string without zero width spaces
1731
+ NSString *stringToBeEmitted = [[textView.textStorage.string
1732
+ stringByReplacingOccurrencesOfString:@"\u200B"
1733
+ withString:@""] copy];
1735
1734
 
1736
- emitter->onChangeText({.value = [stringToBeEmitted toCppString]});
1735
+ emitter->onChangeText({.value = [stringToBeEmitted toCppString]});
1736
+ }
1737
1737
  }
1738
1738
  }
1739
1739
  // all the visible (not meta) attributes handling in the ranges that could
@@ -1743,11 +1743,20 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
1743
1743
  [self tryUpdatingHeight];
1744
1744
  // update active styles as well
1745
1745
  [self tryUpdatingActiveStyles];
1746
- [self layoutAttachments];
1747
1746
  // update drawing - schedule debounced relayout
1748
1747
  [self scheduleRelayoutIfNeeded];
1749
1748
  }
1750
1749
 
1750
+ - (void)handlePlaceholderVisibility {
1751
+ // placeholder management
1752
+ if (!_placeholderLabel.hidden && textView.textStorage.string.length > 0) {
1753
+ [self setPlaceholderLabelShown:NO];
1754
+ } else if (textView.textStorage.string.length == 0 &&
1755
+ _placeholderLabel.hidden) {
1756
+ [self setPlaceholderLabelShown:YES];
1757
+ }
1758
+ }
1759
+
1751
1760
  // Debounced relayout helper - coalesces multiple requests into one per runloop
1752
1761
  // tick
1753
1762
  - (void)scheduleRelayoutIfNeeded {
@@ -1783,6 +1792,9 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
1783
1792
  // measureSize may not be up-to date at that point
1784
1793
  CGSize measuredSize = [self measureSize:self->textView.frame.size.width];
1785
1794
  self->textView.contentSize = measuredSize;
1795
+
1796
+ // Re-position attachment image views after the forced full re-layout
1797
+ [self layoutAttachments];
1786
1798
  });
1787
1799
  }
1788
1800
 
@@ -274,69 +274,77 @@ static void const *kInputKey = &kInputKey;
274
274
  [self glyphRangeForCharacterRange:[paragraph rangeValue]
275
275
  actualCharacterRange:nullptr];
276
276
 
277
- [self enumerateLineFragmentsForGlyphRange:paragraphGlyphRange
278
- usingBlock:^(CGRect rect, CGRect usedRect,
279
- NSTextContainer *container,
280
- NSRange lineGlyphRange,
281
- BOOL *stop) {
282
- NSUInteger charIdx =
283
- [self characterIndexForGlyphAtIndex:
284
- lineGlyphRange.location];
285
- UIFont *font = [host.textView.textStorage
286
- attribute:NSFontAttributeName
287
- atIndex:charIdx
288
- effectiveRange:nil];
289
- CGRect textUsedRect =
290
- [self getTextAlignedUsedRect:usedRect
291
- font:font];
292
-
293
- for (NSTextList *list in pStyle
294
- .textLists) {
295
- NSString *markerFormat =
296
- list.markerFormat;
297
-
298
- if ([markerFormat
299
- hasPrefix:
300
- @"EnrichedAlignment"]) {
301
- continue;
302
- }
303
-
304
- if ([markerFormat
305
- isEqualToString:
306
- @"EnrichedOrderedList"]) {
307
- NSString *marker = [self
308
- getDecimalMarkerForList:host
309
- charIndex:charIdx];
310
- [self drawDecimal:host
311
- marker:marker
312
- markerAttributes:markerAttributes
313
- origin:origin
314
- usedRect:usedRect
315
- indent:indent];
316
- } else if ([markerFormat
317
- isEqualToString:
318
- @"EnrichedUnordered"
319
- @"Lis"
320
- @"t"]) {
321
- [self drawBullet:host
322
- origin:origin
323
- usedRect:textUsedRect
324
- indent:indent];
277
+ [self
278
+ enumerateLineFragmentsForGlyphRange:paragraphGlyphRange
279
+ usingBlock:^(CGRect rect, CGRect usedRect,
280
+ NSTextContainer *container,
281
+ NSRange lineGlyphRange,
282
+ BOOL *stop) {
283
+ NSUInteger charIdx =
284
+ [self characterIndexForGlyphAtIndex:
285
+ lineGlyphRange.location];
286
+ UIFont *font = [host.textView.textStorage
287
+ attribute:NSFontAttributeName
288
+ atIndex:charIdx
289
+ effectiveRange:nil];
290
+ CGRect textUsedRect =
291
+ [self getTextAlignedUsedRect:usedRect
292
+ font:font];
293
+
294
+ for (NSTextList *list in pStyle
295
+ .textLists) {
296
+ NSString *markerFormat =
297
+ list.markerFormat;
298
+
299
+ if ([markerFormat
300
+ hasPrefix:
301
+ @"EnrichedAlignment"]) {
302
+ continue;
303
+ }
325
304
 
326
- } else if ([markerFormat
327
- hasPrefix:@"EnrichedChe"
328
- @"ckbox"]) {
329
- [self drawCheckbox:host
330
- markerFormat:markerFormat
305
+ if ([markerFormat
306
+ isEqualToString:
307
+ @"EnrichedOrderedList"]) {
308
+ CGPoint glyphLocation =
309
+ [self locationForGlyphAtIndex:
310
+ lineGlyphRange.location];
311
+ CGFloat absoluteBaselineY =
312
+ origin.y + rect.origin.y +
313
+ glyphLocation.y;
314
+ NSString *marker = [self
315
+ getDecimalMarkerForList:host
316
+ charIndex:charIdx];
317
+
318
+ [self drawDecimal:host
319
+ marker:marker
320
+ markerAttributes:markerAttributes
331
321
  origin:origin
332
- usedRect:textUsedRect
322
+ baselineY:absoluteBaselineY
333
323
  indent:indent];
334
- }
324
+ } else if ([markerFormat
325
+ isEqualToString:
326
+ @"EnrichedUnordered"
327
+ @"Lis"
328
+ @"t"]) {
329
+ [self drawBullet:host
330
+ origin:origin
331
+ usedRect:textUsedRect
332
+ indent:indent];
333
+
334
+ } else if ([markerFormat
335
+ hasPrefix:@"EnrichedChe"
336
+ @"ckbox"]) {
337
+ [self drawCheckbox:host
338
+ markerFormat:markerFormat
339
+ origin:origin
340
+ usedRect:textUsedRect
341
+ indent:indent];
335
342
  }
336
- // only first line of a list gets its
337
- // marker drawn
338
- *stop = YES;
339
- }];
343
+ }
344
+ // only first line of a list gets its
345
+ // marker drawn
346
+ *stop = YES;
347
+ }];
340
348
  }
341
349
  }
342
350
  }
@@ -439,13 +447,16 @@ static void const *kInputKey = &kInputKey;
439
447
  marker:(NSString *)marker
440
448
  markerAttributes:(NSDictionary *)markerAttributes
441
449
  origin:(CGPoint)origin
442
- usedRect:(CGRect)usedRect
450
+ baselineY:(CGFloat)baselineY
443
451
  indent:(CGFloat)indent {
444
452
  CGFloat gapWidth = [host.config orderedListGapWidth];
445
453
  CGSize markerSize = [marker sizeWithAttributes:markerAttributes];
446
454
  CGFloat markerX = origin.x + indent - gapWidth - markerSize.width / 2;
447
- CGFloat centerY = CGRectGetMidY(usedRect) + origin.y;
448
- CGFloat markerY = centerY - markerSize.height / 2.0;
455
+ UIFont *markerFont = markerAttributes[NSFontAttributeName];
456
+
457
+ // drawAtPoint draws from the top-left bounding box of the string.
458
+ // (baselineY - ascender) exactly pinpoints the top edge of the text
459
+ CGFloat markerY = baselineY - markerFont.ascender;
449
460
 
450
461
  [marker drawAtPoint:CGPointMake(markerX, markerY)
451
462
  withAttributes:markerAttributes];
@@ -51,6 +51,81 @@
51
51
  }
52
52
  }
53
53
 
54
+ // Numeric character references: &#NNNN; (decimal) and &#xHHHH; (hex)
55
+ NSRegularExpression *numericEntityRegex = [NSRegularExpression
56
+ regularExpressionWithPattern:@"&#([xX][0-9a-fA-F]+|[0-9]+);"
57
+ options:0
58
+ error:nil];
59
+
60
+ [numericEntityRegex
61
+ enumerateMatchesInString:text
62
+ options:0
63
+ range:NSMakeRange(0, text.length)
64
+ usingBlock:^(NSTextCheckingResult *match,
65
+ NSMatchingFlags flags, BOOL *stop) {
66
+ if (match == nil) {
67
+ return;
68
+ }
69
+ NSRange fullRange = [match range];
70
+ NSString *entityStr = [text substringWithRange:fullRange];
71
+ NSString *valueStr =
72
+ [text substringWithRange:[match rangeAtIndex:1]];
73
+
74
+ // Convert the matched string into a raw integer (UTF32
75
+ // Code Point)
76
+ UTF32Char codePoint = 0;
77
+ if ([valueStr hasPrefix:@"x"] ||
78
+ [valueStr hasPrefix:@"X"]) {
79
+ // Parse Hexadecimal (base 16)
80
+ const char *hexStr =
81
+ [[valueStr substringFromIndex:1] UTF8String];
82
+ codePoint = (UTF32Char)strtoul(hexStr, NULL, 16);
83
+ } else {
84
+ // Parse Decimal (base 10)
85
+ const char *decStr = [valueStr UTF8String];
86
+ codePoint = (UTF32Char)strtoul(decStr, NULL, 10);
87
+ }
88
+
89
+ // Safety check: HTML numeric character references should
90
+ // map to valid Unicode scalar values (0x0..0x10FFFF),
91
+ // excluding surrogate code points (0xD800-0xDFFF). Per
92
+ // HTML5, code point 0 is treated as U+FFFD. Replace
93
+ // invalid values with U+FFFD (Replacement Character) to
94
+ // avoid crashes/truncation.
95
+ if (codePoint == 0 || codePoint > 0x10FFFF ||
96
+ (codePoint >= 0xD800 && codePoint <= 0xDFFF)) {
97
+ codePoint = 0xFFFD;
98
+ }
99
+
100
+ NSString *decoded;
101
+ if (codePoint <= 0xFFFF) {
102
+ // STANDARD CHARACTER: Fits perfectly in one 16-bit
103
+ // unichar.
104
+ unichar ch = (unichar)codePoint;
105
+ decoded = [NSString stringWithCharacters:&ch length:1];
106
+ } else {
107
+ // LARGE CHARACTER: Too big for 16 bits.
108
+ // We must split the code point into two 16-bit halves
109
+ // (a "Surrogate Pair") so NSString can store it
110
+ // properly in UTF-16.
111
+ UniChar surrogate[2];
112
+
113
+ // Calculate the "High" surrogate half
114
+ surrogate[0] =
115
+ (UniChar)(0xD800 + ((codePoint - 0x10000) >> 10));
116
+
117
+ // Calculate the "Low" surrogate half
118
+ surrogate[1] =
119
+ (UniChar)(0xDC00 + ((codePoint - 0x10000) & 0x3FF));
120
+
121
+ // Create the string using both 16-bit pieces
122
+ decoded = [NSString stringWithCharacters:surrogate
123
+ length:2];
124
+ }
125
+
126
+ results[@(fullRange.location)] = @[ entityStr, decoded ];
127
+ }];
128
+
54
129
  return results;
55
130
  }
56
131
 
@@ -2,10 +2,13 @@
2
2
  #import "EnrichedViewHost.h"
3
3
  #import <UIKit/UIKit.h>
4
4
 
5
+ @class EnrichedConfig;
6
+
5
7
  @interface HtmlParser : NSObject
6
8
  + (NSString *_Nullable)initiallyProcessHtml:(NSString *_Nonnull)html
7
9
  useHtmlNormalizer:(BOOL)useHtmlNormalizer;
8
- + (NSArray *_Nonnull)getTextAndStylesFromHtml:(NSString *_Nonnull)fixedHtml;
10
+ + (NSArray *_Nonnull)getTextAndStylesFromHtml:(NSString *_Nonnull)fixedHtml
11
+ config:(EnrichedConfig *_Nullable)config;
9
12
  + (NSString *_Nonnull)parseToHtmlFromRange:(NSRange)range
10
13
  host:(id<EnrichedViewHost>)host;
11
14
  @end
@@ -1,6 +1,7 @@
1
1
  #import "HtmlParser.h"
2
2
  #import "AlignmentEntry.h"
3
3
  #import "AlignmentUtils.h"
4
+ #import "EnrichedConfig.h"
4
5
  #import "ImageData.h"
5
6
  #import "LinkData.h"
6
7
  #import "MentionParams.h"
@@ -414,7 +415,9 @@
414
415
  return fixedHtml;
415
416
  }
416
417
 
417
- + (NSArray *_Nonnull)getTextAndStylesFromHtml:(NSString *_Nonnull)fixedHtml {
418
+ + (NSArray *_Nonnull)getTextAndStylesFromHtml:(NSString *_Nonnull)fixedHtml
419
+ config:
420
+ (EnrichedConfig *_Nullable)config {
418
421
  NSMutableString *plainText = [[NSMutableString alloc] initWithString:@""];
419
422
  NSMutableDictionary *ongoingTags = [[NSMutableDictionary alloc] init];
420
423
  NSMutableArray *initiallyProcessedTags = [[NSMutableArray alloc] init];
@@ -645,6 +648,10 @@
645
648
 
646
649
  // process tags into proper StyleType + StylePair values
647
650
  NSMutableArray *processedStyles = [[NSMutableArray alloc] init];
651
+ // Tracks the number of processed images to remove their pre-generated
652
+ // placeholder offsets from tag ranges when reading from plainText
653
+ // (which does not contain those placeholders).
654
+ NSInteger secondPassImageCount = 0;
648
655
 
649
656
  for (NSArray *arr in initiallyProcessedTags) {
650
657
  NSString *tagName = (NSString *)arr[0];
@@ -715,6 +722,7 @@
715
722
  }
716
723
 
717
724
  stylePair.styleValue = imageData;
725
+ secondPassImageCount++;
718
726
  } else if ([tagName isEqualToString:@"u"]) {
719
727
  [styleArr addObject:@([UnderlineStyle getType])];
720
728
  } else if ([tagName isEqualToString:@"s"]) {
@@ -742,12 +750,21 @@
742
750
  NSString *url =
743
751
  [params substringWithRange:NSMakeRange(hrefRange.location + 6,
744
752
  hrefRange.length - 7)];
745
- NSString *text = [plainText substringWithRange:tagRangeValue.rangeValue];
753
+
754
+ // tagRange location includes one extra offset per preceding image
755
+ // placeholder, which don't exist in plainText. Subtract them to map
756
+ // back to the correct plainText index.
757
+ NSRange adjustedRange = tagRangeValue.rangeValue;
758
+ NSRange plainTextRange = NSMakeRange(
759
+ adjustedRange.location - secondPassImageCount, adjustedRange.length);
760
+ NSString *text = [plainText substringWithRange:plainTextRange];
746
761
 
747
762
  LinkData *linkData = [[LinkData alloc] init];
748
763
  linkData.url = url;
749
764
  linkData.text = text;
750
- linkData.isManual = ![text isEqualToString:url];
765
+ linkData.isManual = ![text isEqualToString:url] ||
766
+ ![LinkStyle matchesLinkRegexWithConfig:url
767
+ config:config];
751
768
 
752
769
  stylePair.styleValue = linkData;
753
770
  } else if ([tagName isEqualToString:@"mention"]) {
@@ -1315,8 +1332,9 @@
1315
1332
  ImageData *data = [imageStyle getImageDataAt:location];
1316
1333
  if (data != nullptr && data.uri != nullptr) {
1317
1334
  return [NSString
1318
- stringWithFormat:@"img src=\"%@\" width=\"%f\" height=\"%f\"",
1319
- data.uri, data.width, data.height];
1335
+ stringWithFormat:@"img src=\"%@\" width=\"%d\" height=\"%d\"",
1336
+ data.uri, (int)floor(data.width),
1337
+ (int)floor(data.height)];
1320
1338
  }
1321
1339
  }
1322
1340
  return @"img";
@@ -24,7 +24,8 @@
24
24
  _input->textView.typingAttributes = _input->defaultTypingAttributes;
25
25
 
26
26
  @try {
27
- NSArray *processingResult = [HtmlParser getTextAndStylesFromHtml:html];
27
+ NSArray *processingResult =
28
+ [HtmlParser getTextAndStylesFromHtml:html config:_input.config];
28
29
  NSString *plainText = (NSString *)processingResult[0];
29
30
  NSArray *stylesInfo = (NSArray *)processingResult[1];
30
31
  NSArray *alignments = (NSArray *)processingResult[2];
@@ -50,7 +51,8 @@
50
51
 
51
52
  - (void)replaceFromHtml:(NSString *_Nonnull)html range:(NSRange)range {
52
53
  @try {
53
- NSArray *processingResult = [HtmlParser getTextAndStylesFromHtml:html];
54
+ NSArray *processingResult =
55
+ [HtmlParser getTextAndStylesFromHtml:html config:_input.config];
54
56
  NSString *plainText = (NSString *)processingResult[0];
55
57
  NSArray *stylesInfo = (NSArray *)processingResult[1];
56
58
  NSArray *alignments = (NSArray *)processingResult[2];
@@ -81,7 +83,8 @@
81
83
 
82
84
  - (void)insertFromHtml:(NSString *_Nonnull)html location:(NSInteger)location {
83
85
  @try {
84
- NSArray *processingResult = [HtmlParser getTextAndStylesFromHtml:html];
86
+ NSArray *processingResult =
87
+ [HtmlParser getTextAndStylesFromHtml:html config:_input.config];
85
88
  NSString *plainText = (NSString *)processingResult[0];
86
89
  NSArray *stylesInfo = (NSArray *)processingResult[1];
87
90
  NSArray *alignments = (NSArray *)processingResult[2];