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.
- package/android/src/main/java/com/swmansion/enriched/common/EnrichedSpanFlags.kt +39 -0
- package/android/src/main/java/com/swmansion/enriched/common/parser/EnrichedParser.java +43 -20
- package/android/src/main/java/com/swmansion/enriched/common/parser/EnrichedSpanFactory.kt +1 -0
- package/android/src/main/java/com/swmansion/enriched/text/EnrichedTextSpanFactory.kt +1 -0
- package/android/src/main/java/com/swmansion/enriched/text/EnrichedTextView.kt +2 -1
- package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputSpannableFactory.kt +2 -1
- package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputView.kt +8 -5
- package/android/src/main/java/com/swmansion/enriched/textinput/events/MentionHandler.kt +12 -2
- package/android/src/main/java/com/swmansion/enriched/textinput/events/OnMentionEvent.kt +6 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/styles/AlignmentStyles.kt +6 -7
- package/android/src/main/java/com/swmansion/enriched/textinput/styles/InlineStyles.kt +13 -1
- package/android/src/main/java/com/swmansion/enriched/textinput/styles/ListStyles.kt +4 -3
- package/android/src/main/java/com/swmansion/enriched/textinput/styles/ParagraphStyles.kt +4 -3
- package/android/src/main/java/com/swmansion/enriched/textinput/styles/ParametrizedStyles.kt +38 -11
- package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedSelection.kt +2 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedSpannable.kt +2 -1
- package/android/src/main/java/com/swmansion/enriched/textinput/utils/Utils.kt +2 -1
- package/cpp/parser/GumboNormalizer.c +61 -3
- package/cpp/tests/GumboParserTest.cpp +54 -0
- package/ios/EnrichedTextInputView.mm +37 -25
- package/ios/extensions/LayoutManagerExtension.mm +73 -62
- package/ios/extensions/StringExtension.mm +75 -0
- package/ios/htmlParser/HtmlParser.h +4 -1
- package/ios/htmlParser/HtmlParser.mm +23 -5
- package/ios/inputHtmlParser/InputHtmlParser.mm +6 -3
- package/ios/interfaces/StyleHeaders.h +4 -0
- package/ios/styles/LinkStyle.mm +20 -34
- package/ios/styles/MentionStyle.mm +55 -4
- package/ios/textHtmlParser/TextHtmlParser.mm +2 -1
- package/ios/utils/ParagraphAttributesUtils.mm +7 -0
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/utils/expandMentionStylesForIndicators.js +2 -3
- package/lib/module/utils/expandMentionStylesForIndicators.js.map +1 -1
- package/lib/module/utils/normalizeHtmlStyle.js +1 -1
- package/lib/module/utils/normalizeHtmlStyle.js.map +1 -1
- package/lib/module/web/EnrichedText.css +343 -0
- package/lib/module/web/EnrichedText.js +55 -0
- package/lib/module/web/EnrichedText.js.map +1 -0
- package/lib/module/web/EnrichedTextInput.js +54 -52
- package/lib/module/web/EnrichedTextInput.js.map +1 -1
- package/lib/module/web/constants/brokenImageGlyph.js +9 -0
- package/lib/module/web/constants/brokenImageGlyph.js.map +1 -0
- package/lib/module/web/constants/classNames.js +5 -0
- package/lib/module/web/constants/classNames.js.map +1 -0
- package/lib/module/web/emitLinkDetected.js +12 -0
- package/lib/module/web/emitLinkDetected.js.map +1 -0
- package/lib/module/web/formats/EnrichedBlockquote.js +1 -1
- package/lib/module/web/formats/EnrichedBlockquote.js.map +1 -1
- package/lib/module/web/formats/EnrichedCheckboxList.js +2 -2
- package/lib/module/web/formats/EnrichedCheckboxList.js.map +1 -1
- package/lib/module/web/formats/EnrichedCodeBlock.js +1 -1
- package/lib/module/web/formats/EnrichedCodeBlock.js.map +1 -1
- package/lib/module/web/formats/EnrichedHeading.js +1 -1
- package/lib/module/web/formats/EnrichedHeading.js.map +1 -1
- package/lib/module/web/formats/EnrichedImageNodeView.js +1 -1
- package/lib/module/web/formats/EnrichedImageNodeView.js.map +1 -1
- package/lib/module/web/formats/EnrichedLink.js +43 -3
- package/lib/module/web/formats/EnrichedLink.js.map +1 -1
- package/lib/module/web/formats/EnrichedOrderedList.js +2 -2
- package/lib/module/web/formats/EnrichedOrderedList.js.map +1 -1
- package/lib/module/web/formats/EnrichedTextAlign.js +82 -0
- package/lib/module/web/formats/EnrichedTextAlign.js.map +1 -0
- package/lib/module/web/formats/EnrichedUnorderedList.js +2 -2
- package/lib/module/web/formats/EnrichedUnorderedList.js.map +1 -1
- package/lib/module/web/formats/applyWrappingListToSelection.js +3 -2
- package/lib/module/web/formats/applyWrappingListToSelection.js.map +1 -1
- package/lib/module/web/formats/formatRules.js +39 -3
- package/lib/module/web/formats/formatRules.js.map +1 -1
- package/lib/module/web/formats/listKeyboard.js +4 -1
- package/lib/module/web/formats/listKeyboard.js.map +1 -1
- package/lib/module/web/normalization/checkboxHtmlNormalizer.js.map +1 -0
- package/lib/module/web/normalization/htmlNormalizer.js +514 -0
- package/lib/module/web/normalization/htmlNormalizer.js.map +1 -0
- package/lib/module/web/normalization/prepareHtmlForWeb.js +78 -0
- package/lib/module/web/normalization/prepareHtmlForWeb.js.map +1 -0
- package/lib/module/web/{tiptapHtmlNormalizer.js → normalization/tiptapHtmlNormalizer.js} +6 -2
- package/lib/module/web/normalization/tiptapHtmlNormalizer.js.map +1 -0
- package/lib/module/web/pmPlugins/AutolinkPlugin/autolinkRegex.js +27 -0
- package/lib/module/web/pmPlugins/AutolinkPlugin/autolinkRegex.js.map +1 -0
- package/lib/module/web/pmPlugins/AutolinkPlugin/index.js +178 -0
- package/lib/module/web/pmPlugins/AutolinkPlugin/index.js.map +1 -0
- package/lib/module/web/pmPlugins/MentionPlugin/index.js +1 -1
- package/lib/module/web/pmPlugins/MentionPlugin/index.js.map +1 -1
- package/lib/module/web/pmPlugins/MentionPlugin/makeMentionPluginState.js +23 -4
- package/lib/module/web/pmPlugins/MentionPlugin/makeMentionPluginState.js.map +1 -1
- package/lib/module/web/pmPlugins/MentionPlugin/setMention.js +17 -18
- package/lib/module/web/pmPlugins/MentionPlugin/setMention.js.map +1 -1
- package/lib/module/web/pmPlugins/MentionPlugin/useMentionEvents.js +105 -0
- package/lib/module/web/pmPlugins/MentionPlugin/useMentionEvents.js.map +1 -0
- package/lib/module/web/pmPlugins/ShortcutPlugin.js +11 -0
- package/lib/module/web/pmPlugins/ShortcutPlugin.js.map +1 -1
- package/lib/module/web/pmPlugins/StrictMarksPlugin.js +2 -0
- package/lib/module/web/pmPlugins/StrictMarksPlugin.js.map +1 -1
- package/lib/module/web/pmPlugins/StripBoldInStyledHeadingsPlugin.js.map +1 -1
- package/lib/module/web/sanitization/htmlSanitizer.js +10 -0
- package/lib/module/web/sanitization/htmlSanitizer.js.map +1 -0
- package/lib/module/web/styleConversion/buildMentionRulesCSS.js +12 -6
- package/lib/module/web/styleConversion/buildMentionRulesCSS.js.map +1 -1
- package/lib/module/web/styleConversion/enrichedBaseStyleToCSSProperties.js +159 -0
- package/lib/module/web/styleConversion/enrichedBaseStyleToCSSProperties.js.map +1 -0
- package/lib/module/web/styleConversion/enrichedInputStyleToCSSProperties.js +3 -155
- package/lib/module/web/styleConversion/enrichedInputStyleToCSSProperties.js.map +1 -1
- package/lib/module/web/styleConversion/enrichedTextStyleToCSSProperties.js +44 -0
- package/lib/module/web/styleConversion/enrichedTextStyleToCSSProperties.js.map +1 -0
- package/lib/module/web/styleConversion/{enrichedInputThemingToCSSProperties.js → enrichedThemingToCSSProperties.js} +3 -3
- package/lib/module/web/styleConversion/enrichedThemingToCSSProperties.js.map +1 -0
- package/lib/module/web/styleConversion/htmlStyleToCSSVariables.js +87 -64
- package/lib/module/web/styleConversion/htmlStyleToCSSVariables.js.map +1 -1
- package/lib/module/web/styleConversion/inlineImageCSSVariables.js +7 -0
- package/lib/module/web/styleConversion/inlineImageCSSVariables.js.map +1 -0
- package/lib/module/web/useImageErrorFallback.js +33 -0
- package/lib/module/web/useImageErrorFallback.js.map +1 -0
- package/lib/module/web/useOnChangeHtml.js +1 -1
- package/lib/module/web/useOnChangeHtml.js.map +1 -1
- package/lib/module/web/useOnChangeState.js +6 -3
- package/lib/module/web/useOnChangeState.js.map +1 -1
- package/lib/module/web/useOnLinkDetected.js +32 -45
- package/lib/module/web/useOnLinkDetected.js.map +1 -1
- package/lib/module/web/usePressInteractions.js +19 -0
- package/lib/module/web/usePressInteractions.js.map +1 -0
- package/lib/module/web/useStableRef.js +13 -0
- package/lib/module/web/useStableRef.js.map +1 -0
- package/lib/typescript/src/index.d.ts +1 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/native/EnrichedText.d.ts +1 -1
- package/lib/typescript/src/native/EnrichedText.d.ts.map +1 -1
- package/lib/typescript/src/native/EnrichedTextInput.d.ts +1 -1
- package/lib/typescript/src/native/EnrichedTextInput.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +1 -1
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/utils/expandMentionStylesForIndicators.d.ts +1 -1
- package/lib/typescript/src/utils/expandMentionStylesForIndicators.d.ts.map +1 -1
- package/lib/typescript/src/utils/normalizeHtmlStyle.d.ts.map +1 -1
- package/lib/typescript/src/web/EnrichedText.d.ts +4 -0
- package/lib/typescript/src/web/EnrichedText.d.ts.map +1 -0
- package/lib/typescript/src/web/EnrichedTextInput.d.ts +2 -2
- package/lib/typescript/src/web/EnrichedTextInput.d.ts.map +1 -1
- package/lib/typescript/src/web/constants/brokenImageGlyph.d.ts +3 -0
- package/lib/typescript/src/web/constants/brokenImageGlyph.d.ts.map +1 -0
- package/lib/typescript/src/web/constants/classNames.d.ts +3 -0
- package/lib/typescript/src/web/constants/classNames.d.ts.map +1 -0
- package/lib/typescript/src/web/emitLinkDetected.d.ts +10 -0
- package/lib/typescript/src/web/emitLinkDetected.d.ts.map +1 -0
- package/lib/typescript/src/web/formats/EnrichedBlockquote.d.ts.map +1 -1
- package/lib/typescript/src/web/formats/EnrichedCheckboxList.d.ts.map +1 -1
- package/lib/typescript/src/web/formats/EnrichedCodeBlock.d.ts.map +1 -1
- package/lib/typescript/src/web/formats/EnrichedHeading.d.ts.map +1 -1
- package/lib/typescript/src/web/formats/EnrichedImageNodeView.d.ts +1 -1
- package/lib/typescript/src/web/formats/EnrichedImageNodeView.d.ts.map +1 -1
- package/lib/typescript/src/web/formats/EnrichedLink.d.ts +4 -1
- package/lib/typescript/src/web/formats/EnrichedLink.d.ts.map +1 -1
- package/lib/typescript/src/web/formats/EnrichedOrderedList.d.ts.map +1 -1
- package/lib/typescript/src/web/formats/EnrichedTextAlign.d.ts +2 -0
- package/lib/typescript/src/web/formats/EnrichedTextAlign.d.ts.map +1 -0
- package/lib/typescript/src/web/formats/EnrichedUnorderedList.d.ts.map +1 -1
- package/lib/typescript/src/web/formats/applyWrappingListToSelection.d.ts.map +1 -1
- package/lib/typescript/src/web/formats/formatRules.d.ts +3 -1
- package/lib/typescript/src/web/formats/formatRules.d.ts.map +1 -1
- package/lib/typescript/src/web/formats/listKeyboard.d.ts.map +1 -1
- package/lib/typescript/src/web/normalization/checkboxHtmlNormalizer.d.ts.map +1 -0
- package/lib/typescript/src/web/normalization/htmlNormalizer.d.ts +2 -0
- package/lib/typescript/src/web/normalization/htmlNormalizer.d.ts.map +1 -0
- package/lib/typescript/src/web/normalization/prepareHtmlForWeb.d.ts +2 -0
- package/lib/typescript/src/web/normalization/prepareHtmlForWeb.d.ts.map +1 -0
- package/lib/typescript/src/web/normalization/tiptapHtmlNormalizer.d.ts +3 -0
- package/lib/typescript/src/web/normalization/tiptapHtmlNormalizer.d.ts.map +1 -0
- package/lib/typescript/src/web/pmPlugins/AutolinkPlugin/autolinkRegex.d.ts +7 -0
- package/lib/typescript/src/web/pmPlugins/AutolinkPlugin/autolinkRegex.d.ts.map +1 -0
- package/lib/typescript/src/web/pmPlugins/AutolinkPlugin/index.d.ts +8 -0
- package/lib/typescript/src/web/pmPlugins/AutolinkPlugin/index.d.ts.map +1 -0
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/index.d.ts +1 -1
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/index.d.ts.map +1 -1
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/makeMentionPluginState.d.ts.map +1 -1
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/setMention.d.ts.map +1 -1
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/useMentionEvents.d.ts +4 -0
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/useMentionEvents.d.ts.map +1 -0
- package/lib/typescript/src/web/pmPlugins/ShortcutPlugin.d.ts.map +1 -1
- package/lib/typescript/src/web/pmPlugins/StrictMarksPlugin.d.ts.map +1 -1
- package/lib/typescript/src/web/pmPlugins/StripBoldInStyledHeadingsPlugin.d.ts +6 -1
- package/lib/typescript/src/web/pmPlugins/StripBoldInStyledHeadingsPlugin.d.ts.map +1 -1
- package/lib/typescript/src/web/sanitization/htmlSanitizer.d.ts +2 -0
- package/lib/typescript/src/web/sanitization/htmlSanitizer.d.ts.map +1 -0
- package/lib/typescript/src/web/styleConversion/buildMentionRulesCSS.d.ts +2 -2
- package/lib/typescript/src/web/styleConversion/buildMentionRulesCSS.d.ts.map +1 -1
- package/lib/typescript/src/web/styleConversion/enrichedBaseStyleToCSSProperties.d.ts +8 -0
- package/lib/typescript/src/web/styleConversion/enrichedBaseStyleToCSSProperties.d.ts.map +1 -0
- package/lib/typescript/src/web/styleConversion/enrichedInputStyleToCSSProperties.d.ts +2 -5
- package/lib/typescript/src/web/styleConversion/enrichedInputStyleToCSSProperties.d.ts.map +1 -1
- package/lib/typescript/src/web/styleConversion/enrichedTextStyleToCSSProperties.d.ts +4 -0
- package/lib/typescript/src/web/styleConversion/enrichedTextStyleToCSSProperties.d.ts.map +1 -0
- package/lib/typescript/src/web/styleConversion/{enrichedInputThemingToCSSProperties.d.ts → enrichedThemingToCSSProperties.d.ts} +1 -1
- package/lib/typescript/src/web/styleConversion/enrichedThemingToCSSProperties.d.ts.map +1 -0
- package/lib/typescript/src/web/styleConversion/htmlStyleToCSSVariables.d.ts +5 -4
- package/lib/typescript/src/web/styleConversion/htmlStyleToCSSVariables.d.ts.map +1 -1
- package/lib/typescript/src/web/styleConversion/inlineImageCSSVariables.d.ts +3 -0
- package/lib/typescript/src/web/styleConversion/inlineImageCSSVariables.d.ts.map +1 -0
- package/lib/typescript/src/web/useImageErrorFallback.d.ts +3 -0
- package/lib/typescript/src/web/useImageErrorFallback.d.ts.map +1 -0
- package/lib/typescript/src/web/useOnChangeState.d.ts.map +1 -1
- package/lib/typescript/src/web/useOnLinkDetected.d.ts +2 -2
- package/lib/typescript/src/web/useOnLinkDetected.d.ts.map +1 -1
- package/lib/typescript/src/web/usePressInteractions.d.ts +2 -0
- package/lib/typescript/src/web/usePressInteractions.d.ts.map +1 -0
- package/lib/typescript/src/web/useStableRef.d.ts +3 -0
- package/lib/typescript/src/web/useStableRef.d.ts.map +1 -0
- package/package.json +54 -28
- package/src/index.tsx +1 -0
- package/src/types.ts +5 -3
- package/src/utils/expandMentionStylesForIndicators.ts +3 -3
- package/src/utils/normalizeHtmlStyle.ts +2 -1
- package/src/web/EnrichedText.css +343 -0
- package/src/web/EnrichedText.tsx +82 -0
- package/src/web/EnrichedTextInput.tsx +69 -55
- package/src/web/constants/brokenImageGlyph.ts +7 -0
- package/src/web/constants/classNames.ts +2 -0
- package/src/web/emitLinkDetected.ts +29 -0
- package/src/web/formats/EnrichedBlockquote.ts +2 -1
- package/src/web/formats/EnrichedCheckboxList.ts +5 -2
- package/src/web/formats/EnrichedCodeBlock.ts +2 -1
- package/src/web/formats/EnrichedHeading.ts +2 -1
- package/src/web/formats/EnrichedImageNodeView.tsx +1 -3
- package/src/web/formats/EnrichedLink.ts +59 -4
- package/src/web/formats/EnrichedOrderedList.ts +5 -2
- package/src/web/formats/EnrichedTextAlign.ts +88 -0
- package/src/web/formats/EnrichedUnorderedList.ts +5 -2
- package/src/web/formats/applyWrappingListToSelection.ts +4 -4
- package/src/web/formats/formatRules.ts +48 -4
- package/src/web/formats/listKeyboard.ts +6 -1
- package/src/web/normalization/htmlNormalizer.ts +656 -0
- package/src/web/normalization/prepareHtmlForWeb.ts +93 -0
- package/src/web/{tiptapHtmlNormalizer.ts → normalization/tiptapHtmlNormalizer.ts} +12 -2
- package/src/web/pmPlugins/AutolinkPlugin/autolinkRegex.ts +43 -0
- package/src/web/pmPlugins/AutolinkPlugin/index.ts +274 -0
- package/src/web/pmPlugins/MentionPlugin/index.ts +1 -1
- package/src/web/pmPlugins/MentionPlugin/makeMentionPluginState.ts +32 -5
- package/src/web/pmPlugins/MentionPlugin/setMention.ts +19 -29
- package/src/web/pmPlugins/MentionPlugin/useMentionEvents.ts +114 -0
- package/src/web/pmPlugins/ShortcutPlugin.ts +7 -0
- package/src/web/pmPlugins/StrictMarksPlugin.ts +3 -0
- package/src/web/pmPlugins/StripBoldInStyledHeadingsPlugin.ts +41 -36
- package/src/web/sanitization/htmlSanitizer.ts +8 -0
- package/src/web/styleConversion/buildMentionRulesCSS.ts +30 -11
- package/src/web/styleConversion/enrichedBaseStyleToCSSProperties.ts +228 -0
- package/src/web/styleConversion/enrichedInputStyleToCSSProperties.ts +6 -217
- package/src/web/styleConversion/enrichedTextStyleToCSSProperties.ts +53 -0
- package/src/web/styleConversion/{enrichedInputThemingToCSSProperties.ts → enrichedThemingToCSSProperties.ts} +4 -4
- package/src/web/styleConversion/htmlStyleToCSSVariables.ts +121 -70
- package/src/web/styleConversion/inlineImageCSSVariables.ts +6 -0
- package/src/web/useImageErrorFallback.ts +37 -0
- package/src/web/useOnChangeHtml.ts +1 -1
- package/src/web/useOnChangeState.ts +7 -5
- package/src/web/useOnLinkDetected.ts +38 -50
- package/src/web/usePressInteractions.tsx +22 -0
- package/src/web/useStableRef.ts +12 -0
- package/lib/module/web/EnrichedTextInput.css +0 -201
- package/lib/module/web/checkboxHtmlNormalizer.js.map +0 -1
- package/lib/module/web/pmPlugins/MentionPlugin/subscribeMentionEvents.js +0 -100
- package/lib/module/web/pmPlugins/MentionPlugin/subscribeMentionEvents.js.map +0 -1
- package/lib/module/web/styleConversion/enrichedInputThemingToCSSProperties.js.map +0 -1
- package/lib/module/web/tiptapHtmlNormalizer.js.map +0 -1
- package/lib/typescript/src/web/checkboxHtmlNormalizer.d.ts.map +0 -1
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/subscribeMentionEvents.d.ts +0 -4
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/subscribeMentionEvents.d.ts.map +0 -1
- package/lib/typescript/src/web/styleConversion/enrichedInputThemingToCSSProperties.d.ts.map +0 -1
- package/lib/typescript/src/web/tiptapHtmlNormalizer.d.ts +0 -3
- package/lib/typescript/src/web/tiptapHtmlNormalizer.d.ts.map +0 -1
- package/src/web/EnrichedTextInput.css +0 -201
- package/src/web/pmPlugins/MentionPlugin/subscribeMentionEvents.ts +0 -105
- /package/lib/module/web/{checkboxHtmlNormalizer.js → normalization/checkboxHtmlNormalizer.js} +0 -0
- /package/lib/typescript/src/web/{checkboxHtmlNormalizer.d.ts → normalization/checkboxHtmlNormalizer.d.ts} +0 -0
- /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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
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
|
-
|
|
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
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
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
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
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
|
-
|
|
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
|
-
|
|
337
|
-
|
|
338
|
-
|
|
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
|
-
|
|
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
|
-
|
|
448
|
-
|
|
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
|
-
|
|
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=\"%
|
|
1319
|
-
data.uri, data.width,
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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];
|