react-native-enriched 0.2.0 → 0.3.0
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/README.md +16 -17
- package/android/build.gradle +77 -72
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerDelegate.java +21 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerInterface.java +7 -0
- package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/EventEmitters.cpp +156 -0
- package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/EventEmitters.h +147 -0
- package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/Props.cpp +10 -0
- package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/Props.h +194 -0
- package/android/lint.gradle +70 -0
- package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputConnectionWrapper.kt +140 -0
- package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputView.kt +304 -83
- package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewLayoutManager.kt +3 -1
- package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewManager.kt +166 -51
- package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewPackage.kt +1 -3
- package/android/src/main/java/com/swmansion/enriched/MeasurementStore.kt +70 -21
- package/android/src/main/java/com/swmansion/enriched/events/MentionHandler.kt +21 -11
- package/android/src/main/java/com/swmansion/enriched/events/OnChangeHtmlEvent.kt +8 -9
- package/android/src/main/java/com/swmansion/enriched/events/OnChangeSelectionEvent.kt +10 -9
- package/android/src/main/java/com/swmansion/enriched/events/OnChangeStateDeprecatedEvent.kt +21 -0
- package/android/src/main/java/com/swmansion/enriched/events/OnChangeStateEvent.kt +9 -12
- package/android/src/main/java/com/swmansion/enriched/events/OnChangeTextEvent.kt +10 -10
- package/android/src/main/java/com/swmansion/enriched/events/OnInputBlurEvent.kt +7 -9
- package/android/src/main/java/com/swmansion/enriched/events/OnInputFocusEvent.kt +7 -9
- package/android/src/main/java/com/swmansion/enriched/events/OnInputKeyPressEvent.kt +27 -0
- package/android/src/main/java/com/swmansion/enriched/events/OnLinkDetectedEvent.kt +13 -11
- package/android/src/main/java/com/swmansion/enriched/events/OnMentionDetectedEvent.kt +10 -9
- package/android/src/main/java/com/swmansion/enriched/events/OnMentionEvent.kt +9 -8
- package/android/src/main/java/com/swmansion/enriched/events/OnRequestHtmlResultEvent.kt +32 -0
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedBlockQuoteSpan.kt +24 -5
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedBoldSpan.kt +8 -1
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedCodeBlockSpan.kt +10 -2
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH1Span.kt +8 -1
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH2Span.kt +8 -1
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH3Span.kt +8 -1
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH4Span.kt +24 -0
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH5Span.kt +24 -0
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH6Span.kt +24 -0
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedImageSpan.kt +34 -17
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedInlineCodeSpan.kt +8 -1
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedItalicSpan.kt +7 -1
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedLinkSpan.kt +10 -4
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedMentionSpan.kt +14 -11
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedOrderedListSpan.kt +18 -11
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedSpans.kt +174 -72
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedStrikeThroughSpan.kt +7 -1
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedUnderlineSpan.kt +7 -1
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedUnorderedListSpan.kt +11 -5
- package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedBlockSpan.kt +3 -2
- package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedHeadingSpan.kt +1 -2
- package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedInlineSpan.kt +1 -2
- package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedParagraphSpan.kt +3 -2
- package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedSpan.kt +5 -0
- package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedZeroWidthSpaceSpan.kt +1 -2
- package/android/src/main/java/com/swmansion/enriched/spans/utils/ForceRedrawSpan.kt +2 -1
- package/android/src/main/java/com/swmansion/enriched/styles/HtmlStyle.kt +155 -20
- package/android/src/main/java/com/swmansion/enriched/styles/InlineStyles.kt +25 -8
- package/android/src/main/java/com/swmansion/enriched/styles/ListStyles.kt +60 -20
- package/android/src/main/java/com/swmansion/enriched/styles/ParagraphStyles.kt +161 -25
- package/android/src/main/java/com/swmansion/enriched/styles/ParametrizedStyles.kt +128 -52
- package/android/src/main/java/com/swmansion/enriched/utils/AsyncDrawable.kt +10 -7
- package/android/src/main/java/com/swmansion/enriched/utils/EnrichedConstants.kt +11 -0
- package/android/src/main/java/com/swmansion/enriched/utils/EnrichedEditableFactory.kt +17 -0
- package/android/src/main/java/com/swmansion/enriched/utils/EnrichedParser.java +136 -87
- package/android/src/main/java/com/swmansion/enriched/utils/EnrichedSelection.kt +71 -42
- package/android/src/main/java/com/swmansion/enriched/utils/EnrichedSpanState.kt +183 -48
- package/android/src/main/java/com/swmansion/enriched/utils/EnrichedSpannable.kt +82 -0
- package/android/src/main/java/com/swmansion/enriched/utils/EnrichedSpannableStringBuilder.kt +15 -0
- package/android/src/main/java/com/swmansion/enriched/utils/Utils.kt +0 -70
- package/android/src/main/java/com/swmansion/enriched/watchers/EnrichedSpanWatcher.kt +46 -14
- package/android/src/main/java/com/swmansion/enriched/watchers/EnrichedTextWatcher.kt +34 -11
- package/android/src/main/new_arch/CMakeLists.txt +6 -0
- package/android/src/main/new_arch/RNEnrichedTextInputViewSpec.cpp +6 -6
- package/android/src/main/new_arch/RNEnrichedTextInputViewSpec.h +6 -6
- package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputComponentDescriptor.h +19 -19
- package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputMeasurementManager.cpp +40 -51
- package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputMeasurementManager.h +13 -15
- package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputShadowNode.cpp +23 -21
- package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputShadowNode.h +35 -36
- package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputState.cpp +4 -4
- package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputState.h +13 -14
- package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/conversions.h +33 -14
- package/ios/EnrichedTextInputView.h +26 -14
- package/ios/EnrichedTextInputView.mm +1209 -586
- package/ios/config/InputConfig.h +24 -6
- package/ios/config/InputConfig.mm +154 -38
- package/ios/{utils → extensions}/ColorExtension.mm +7 -5
- package/ios/extensions/FontExtension.mm +106 -0
- package/ios/{utils → extensions}/LayoutManagerExtension.h +1 -1
- package/ios/extensions/LayoutManagerExtension.mm +396 -0
- package/ios/{utils → extensions}/StringExtension.mm +19 -16
- package/ios/generated/RNEnrichedTextInputViewSpec/EventEmitters.cpp +156 -0
- package/ios/generated/RNEnrichedTextInputViewSpec/EventEmitters.h +147 -0
- package/ios/generated/RNEnrichedTextInputViewSpec/Props.cpp +10 -0
- package/ios/generated/RNEnrichedTextInputViewSpec/Props.h +194 -0
- package/ios/generated/RNEnrichedTextInputViewSpec/RCTComponentViewHelpers.h +95 -0
- package/ios/inputParser/InputParser.h +5 -5
- package/ios/inputParser/InputParser.mm +864 -380
- package/ios/inputTextView/InputTextView.h +1 -1
- package/ios/inputTextView/InputTextView.mm +100 -59
- package/ios/{utils → interfaces}/BaseStyleProtocol.h +2 -2
- package/ios/interfaces/ImageAttachment.h +10 -0
- package/ios/interfaces/ImageAttachment.mm +36 -0
- package/ios/interfaces/LinkRegexConfig.h +19 -0
- package/ios/interfaces/LinkRegexConfig.mm +37 -0
- package/ios/interfaces/MediaAttachment.h +23 -0
- package/ios/interfaces/MediaAttachment.mm +31 -0
- package/ios/{utils → interfaces}/MentionParams.h +0 -1
- package/ios/{utils → interfaces}/MentionStyleProps.mm +27 -20
- package/ios/{utils → interfaces}/StyleHeaders.h +37 -15
- package/ios/{utils → interfaces}/StyleTypeEnum.h +3 -0
- package/ios/internals/EnrichedTextInputViewComponentDescriptor.h +11 -9
- package/ios/internals/EnrichedTextInputViewShadowNode.h +28 -25
- package/ios/internals/EnrichedTextInputViewShadowNode.mm +45 -40
- package/ios/internals/EnrichedTextInputViewState.h +3 -1
- package/ios/styles/BlockQuoteStyle.mm +189 -118
- package/ios/styles/BoldStyle.mm +110 -63
- package/ios/styles/CodeBlockStyle.mm +204 -128
- package/ios/styles/H1Style.mm +10 -4
- package/ios/styles/H2Style.mm +10 -4
- package/ios/styles/H3Style.mm +10 -4
- package/ios/styles/H4Style.mm +17 -0
- package/ios/styles/H5Style.mm +17 -0
- package/ios/styles/H6Style.mm +17 -0
- package/ios/styles/HeadingStyleBase.mm +148 -86
- package/ios/styles/ImageStyle.mm +75 -73
- package/ios/styles/InlineCodeStyle.mm +162 -88
- package/ios/styles/ItalicStyle.mm +76 -52
- package/ios/styles/LinkStyle.mm +411 -232
- package/ios/styles/MentionStyle.mm +363 -246
- package/ios/styles/OrderedListStyle.mm +171 -106
- package/ios/styles/StrikethroughStyle.mm +52 -35
- package/ios/styles/UnderlineStyle.mm +68 -46
- package/ios/styles/UnorderedListStyle.mm +169 -106
- package/ios/utils/OccurenceUtils.h +42 -42
- package/ios/utils/OccurenceUtils.mm +142 -119
- package/ios/utils/ParagraphAttributesUtils.h +10 -2
- package/ios/utils/ParagraphAttributesUtils.mm +182 -71
- package/ios/utils/ParagraphsUtils.h +2 -1
- package/ios/utils/ParagraphsUtils.mm +41 -27
- package/ios/utils/TextInsertionUtils.h +13 -2
- package/ios/utils/TextInsertionUtils.mm +38 -20
- package/ios/utils/WordsUtils.h +2 -1
- package/ios/utils/WordsUtils.mm +32 -22
- package/ios/utils/ZeroWidthSpaceUtils.h +3 -1
- package/ios/utils/ZeroWidthSpaceUtils.mm +145 -79
- package/lib/module/EnrichedTextInput.js +61 -2
- package/lib/module/EnrichedTextInput.js.map +1 -1
- package/lib/module/EnrichedTextInputNativeComponent.ts +149 -12
- package/lib/module/{normalizeHtmlStyle.js → utils/normalizeHtmlStyle.js} +12 -0
- package/lib/module/utils/normalizeHtmlStyle.js.map +1 -0
- package/lib/module/utils/regexParser.js +46 -0
- package/lib/module/utils/regexParser.js.map +1 -0
- package/lib/typescript/src/EnrichedTextInput.d.ts +24 -14
- package/lib/typescript/src/EnrichedTextInput.d.ts.map +1 -1
- package/lib/typescript/src/EnrichedTextInputNativeComponent.d.ts +129 -12
- package/lib/typescript/src/EnrichedTextInputNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/utils/normalizeHtmlStyle.d.ts +4 -0
- package/lib/typescript/src/utils/normalizeHtmlStyle.d.ts.map +1 -0
- package/lib/typescript/src/utils/regexParser.d.ts +3 -0
- package/lib/typescript/src/utils/regexParser.d.ts.map +1 -0
- package/package.json +17 -6
- package/src/EnrichedTextInput.tsx +96 -13
- package/src/EnrichedTextInputNativeComponent.ts +149 -12
- package/src/index.tsx +2 -0
- package/src/{normalizeHtmlStyle.ts → utils/normalizeHtmlStyle.ts} +14 -2
- package/src/utils/regexParser.ts +56 -0
- package/ios/utils/FontExtension.mm +0 -91
- package/ios/utils/LayoutManagerExtension.mm +0 -286
- package/lib/module/normalizeHtmlStyle.js.map +0 -1
- package/lib/typescript/src/normalizeHtmlStyle.d.ts +0 -4
- package/lib/typescript/src/normalizeHtmlStyle.d.ts.map +0 -1
- package/ios/{utils → extensions}/ColorExtension.h +0 -0
- package/ios/{utils → extensions}/FontExtension.h +0 -0
- package/ios/{utils → extensions}/StringExtension.h +1 -1
- package/ios/{utils → interfaces}/ImageData.h +0 -0
- package/ios/{utils → interfaces}/ImageData.mm +0 -0
- package/ios/{utils → interfaces}/LinkData.h +0 -0
- package/ios/{utils → interfaces}/LinkData.mm +0 -0
- package/ios/{utils → interfaces}/MentionParams.mm +0 -0
- package/ios/{utils → interfaces}/MentionStyleProps.h +1 -1
- /package/ios/{utils → interfaces}/StylePair.h +0 -0
- /package/ios/{utils → interfaces}/StylePair.mm +0 -0
- /package/ios/{utils → interfaces}/TextDecorationLineEnum.h +0 -0
- /package/ios/{utils → interfaces}/TextDecorationLineEnum.mm +0 -0
|
@@ -1,354 +1,481 @@
|
|
|
1
1
|
#import "InputParser.h"
|
|
2
2
|
#import "EnrichedTextInputView.h"
|
|
3
|
+
#import "StringExtension.h"
|
|
3
4
|
#import "StyleHeaders.h"
|
|
4
|
-
#import "UIView+React.h"
|
|
5
5
|
#import "TextInsertionUtils.h"
|
|
6
|
-
#import "
|
|
6
|
+
#import "UIView+React.h"
|
|
7
7
|
|
|
8
8
|
@implementation InputParser {
|
|
9
9
|
EnrichedTextInputView *_input;
|
|
10
|
+
NSInteger _precedingImageCount;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
- (instancetype)initWithInput:(id)input {
|
|
13
14
|
self = [super init];
|
|
14
15
|
_input = (EnrichedTextInputView *)input;
|
|
16
|
+
_precedingImageCount = 0;
|
|
15
17
|
return self;
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
- (NSString *)parseToHtmlFromRange:(NSRange)range {
|
|
19
21
|
NSInteger offset = range.location;
|
|
20
|
-
NSString *text =
|
|
22
|
+
NSString *text =
|
|
23
|
+
[_input->textView.textStorage.string substringWithRange:range];
|
|
21
24
|
|
|
22
|
-
if(text.length == 0) {
|
|
25
|
+
if (text.length == 0) {
|
|
23
26
|
return @"<html>\n<p></p>\n</html>";
|
|
24
27
|
}
|
|
25
28
|
|
|
26
|
-
NSMutableString *result = [[NSMutableString alloc] initWithString
|
|
27
|
-
NSSet<NSNumber
|
|
29
|
+
NSMutableString *result = [[NSMutableString alloc] initWithString:@"<html>"];
|
|
30
|
+
NSSet<NSNumber *> *previousActiveStyles = [[NSSet<NSNumber *> alloc] init];
|
|
28
31
|
BOOL newLine = YES;
|
|
29
32
|
BOOL inUnorderedList = NO;
|
|
30
33
|
BOOL inOrderedList = NO;
|
|
31
34
|
BOOL inBlockQuote = NO;
|
|
32
35
|
BOOL inCodeBlock = NO;
|
|
33
36
|
unichar lastCharacter = 0;
|
|
34
|
-
|
|
35
|
-
for(int i = 0; i < text.length; i++) {
|
|
37
|
+
|
|
38
|
+
for (int i = 0; i < text.length; i++) {
|
|
36
39
|
NSRange currentRange = NSMakeRange(offset + i, 1);
|
|
37
|
-
NSMutableSet<NSNumber
|
|
38
|
-
|
|
39
|
-
|
|
40
|
+
NSMutableSet<NSNumber *> *currentActiveStyles =
|
|
41
|
+
[[NSMutableSet<NSNumber *> alloc] init];
|
|
42
|
+
NSMutableDictionary *currentActiveStylesBeginning =
|
|
43
|
+
[[NSMutableDictionary alloc] init];
|
|
44
|
+
|
|
40
45
|
// check each existing style existence
|
|
41
|
-
for(NSNumber*
|
|
46
|
+
for (NSNumber *type in _input->stylesDict) {
|
|
42
47
|
id<BaseStyleProtocol> style = _input->stylesDict[type];
|
|
43
|
-
if([style detectStyle:currentRange]) {
|
|
48
|
+
if ([style detectStyle:currentRange]) {
|
|
44
49
|
[currentActiveStyles addObject:type];
|
|
45
|
-
|
|
46
|
-
if(![previousActiveStyles member:type]) {
|
|
50
|
+
|
|
51
|
+
if (![previousActiveStyles member:type]) {
|
|
47
52
|
currentActiveStylesBeginning[type] = [NSNumber numberWithInt:i];
|
|
48
53
|
}
|
|
49
|
-
} else if([previousActiveStyles member:type]) {
|
|
54
|
+
} else if ([previousActiveStyles member:type]) {
|
|
50
55
|
[currentActiveStylesBeginning removeObjectForKey:type];
|
|
51
56
|
}
|
|
52
57
|
}
|
|
53
|
-
|
|
54
|
-
NSString *currentCharacterStr =
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
58
|
+
|
|
59
|
+
NSString *currentCharacterStr =
|
|
60
|
+
[_input->textView.textStorage.string substringWithRange:currentRange];
|
|
61
|
+
unichar currentCharacterChar = [_input->textView.textStorage.string
|
|
62
|
+
characterAtIndex:currentRange.location];
|
|
63
|
+
|
|
64
|
+
if ([[NSCharacterSet newlineCharacterSet]
|
|
65
|
+
characterIsMember:currentCharacterChar]) {
|
|
66
|
+
if (newLine) {
|
|
67
|
+
// we can either have an empty list item OR need to close the list and
|
|
68
|
+
// put a BR in such a situation the existence of the list must be
|
|
69
|
+
// checked on 0 length range, not on the newline character
|
|
70
|
+
if (inOrderedList) {
|
|
62
71
|
OrderedListStyle *oStyle = _input->stylesDict[@(OrderedList)];
|
|
63
|
-
BOOL detected =
|
|
64
|
-
|
|
72
|
+
BOOL detected =
|
|
73
|
+
[oStyle detectStyle:NSMakeRange(currentRange.location, 0)];
|
|
74
|
+
if (detected) {
|
|
65
75
|
[result appendString:@"\n<li></li>"];
|
|
66
76
|
} else {
|
|
67
77
|
[result appendString:@"\n</ol>\n<br>"];
|
|
68
78
|
inOrderedList = NO;
|
|
69
79
|
}
|
|
70
|
-
} else if(inUnorderedList) {
|
|
80
|
+
} else if (inUnorderedList) {
|
|
71
81
|
UnorderedListStyle *uStyle = _input->stylesDict[@(UnorderedList)];
|
|
72
|
-
BOOL detected =
|
|
73
|
-
|
|
82
|
+
BOOL detected =
|
|
83
|
+
[uStyle detectStyle:NSMakeRange(currentRange.location, 0)];
|
|
84
|
+
if (detected) {
|
|
74
85
|
[result appendString:@"\n<li></li>"];
|
|
75
86
|
} else {
|
|
76
87
|
[result appendString:@"\n</ul>\n<br>"];
|
|
77
88
|
inUnorderedList = NO;
|
|
78
89
|
}
|
|
90
|
+
} else if (inBlockQuote) {
|
|
91
|
+
BlockQuoteStyle *bqStyle = _input->stylesDict[@(BlockQuote)];
|
|
92
|
+
BOOL detected =
|
|
93
|
+
[bqStyle detectStyle:NSMakeRange(currentRange.location, 0)];
|
|
94
|
+
if (detected) {
|
|
95
|
+
[result appendString:@"\n<br>"];
|
|
96
|
+
} else {
|
|
97
|
+
[result appendString:@"\n</blockquote>\n<br>"];
|
|
98
|
+
inBlockQuote = NO;
|
|
99
|
+
}
|
|
100
|
+
} else if (inCodeBlock) {
|
|
101
|
+
CodeBlockStyle *cbStyle = _input->stylesDict[@(CodeBlock)];
|
|
102
|
+
BOOL detected =
|
|
103
|
+
[cbStyle detectStyle:NSMakeRange(currentRange.location, 0)];
|
|
104
|
+
if (detected) {
|
|
105
|
+
[result appendString:@"\n<br>"];
|
|
106
|
+
} else {
|
|
107
|
+
[result appendString:@"\n</codeblock>\n<br>"];
|
|
108
|
+
inCodeBlock = NO;
|
|
109
|
+
}
|
|
79
110
|
} else {
|
|
80
111
|
[result appendString:@"\n<br>"];
|
|
81
112
|
}
|
|
82
113
|
} else {
|
|
83
114
|
// newline finishes a paragraph and all style tags need to be closed
|
|
84
115
|
// we use previous styles
|
|
85
|
-
NSArray<NSNumber*> *sortedEndedStyles = [previousActiveStyles
|
|
86
|
-
|
|
116
|
+
NSArray<NSNumber *> *sortedEndedStyles = [previousActiveStyles
|
|
117
|
+
sortedArrayUsingDescriptors:@[ [NSSortDescriptor
|
|
118
|
+
sortDescriptorWithKey:@"intValue"
|
|
119
|
+
ascending:NO] ]];
|
|
120
|
+
|
|
87
121
|
// append closing tags
|
|
88
|
-
for(NSNumber *style in sortedEndedStyles) {
|
|
89
|
-
if ([style isEqualToNumber
|
|
122
|
+
for (NSNumber *style in sortedEndedStyles) {
|
|
123
|
+
if ([style isEqualToNumber:@([ImageStyle getStyleType])]) {
|
|
90
124
|
continue;
|
|
91
125
|
}
|
|
92
|
-
NSString *tagContent =
|
|
93
|
-
|
|
126
|
+
NSString *tagContent =
|
|
127
|
+
[self tagContentForStyle:style
|
|
128
|
+
openingTag:NO
|
|
129
|
+
location:currentRange.location];
|
|
130
|
+
[result
|
|
131
|
+
appendString:[NSString stringWithFormat:@"</%@>", tagContent]];
|
|
94
132
|
}
|
|
95
|
-
|
|
133
|
+
|
|
96
134
|
// append closing paragraph tag
|
|
97
|
-
if([previousActiveStyles
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
135
|
+
if ([previousActiveStyles
|
|
136
|
+
containsObject:@([UnorderedListStyle getStyleType])] ||
|
|
137
|
+
[previousActiveStyles
|
|
138
|
+
containsObject:@([OrderedListStyle getStyleType])] ||
|
|
139
|
+
[previousActiveStyles containsObject:@([H1Style getStyleType])] ||
|
|
140
|
+
[previousActiveStyles containsObject:@([H2Style getStyleType])] ||
|
|
141
|
+
[previousActiveStyles containsObject:@([H3Style getStyleType])] ||
|
|
142
|
+
[previousActiveStyles containsObject:@([H4Style getStyleType])] ||
|
|
143
|
+
[previousActiveStyles containsObject:@([H5Style getStyleType])] ||
|
|
144
|
+
[previousActiveStyles containsObject:@([H6Style getStyleType])] ||
|
|
145
|
+
[previousActiveStyles
|
|
146
|
+
containsObject:@([BlockQuoteStyle getStyleType])] ||
|
|
147
|
+
[previousActiveStyles
|
|
148
|
+
containsObject:@([CodeBlockStyle getStyleType])]) {
|
|
149
|
+
// do nothing, proper closing paragraph tags have been already
|
|
150
|
+
// appended
|
|
106
151
|
} else {
|
|
107
152
|
[result appendString:@"</p>"];
|
|
108
153
|
}
|
|
109
154
|
}
|
|
110
|
-
|
|
155
|
+
|
|
111
156
|
// clear the previous styles
|
|
112
|
-
previousActiveStyles = [[NSSet<NSNumber *> alloc]init];
|
|
113
|
-
|
|
157
|
+
previousActiveStyles = [[NSSet<NSNumber *> alloc] init];
|
|
158
|
+
|
|
114
159
|
// next character opens new paragraph
|
|
115
160
|
newLine = YES;
|
|
116
161
|
} else {
|
|
117
162
|
// new line - open the paragraph
|
|
118
|
-
if(newLine) {
|
|
163
|
+
if (newLine) {
|
|
119
164
|
newLine = NO;
|
|
120
|
-
|
|
165
|
+
|
|
121
166
|
// handle ending unordered list
|
|
122
|
-
if(inUnorderedList &&
|
|
167
|
+
if (inUnorderedList &&
|
|
168
|
+
![currentActiveStyles
|
|
169
|
+
containsObject:@([UnorderedListStyle getStyleType])]) {
|
|
123
170
|
inUnorderedList = NO;
|
|
124
171
|
[result appendString:@"\n</ul>"];
|
|
125
172
|
}
|
|
126
173
|
// handle ending ordered list
|
|
127
|
-
if(inOrderedList &&
|
|
174
|
+
if (inOrderedList &&
|
|
175
|
+
![currentActiveStyles
|
|
176
|
+
containsObject:@([OrderedListStyle getStyleType])]) {
|
|
128
177
|
inOrderedList = NO;
|
|
129
178
|
[result appendString:@"\n</ol>"];
|
|
130
179
|
}
|
|
131
180
|
// handle ending blockquotes
|
|
132
|
-
if(inBlockQuote &&
|
|
181
|
+
if (inBlockQuote &&
|
|
182
|
+
![currentActiveStyles
|
|
183
|
+
containsObject:@([BlockQuoteStyle getStyleType])]) {
|
|
133
184
|
inBlockQuote = NO;
|
|
134
185
|
[result appendString:@"\n</blockquote>"];
|
|
135
186
|
}
|
|
136
187
|
// handle ending codeblock
|
|
137
|
-
if(inCodeBlock &&
|
|
188
|
+
if (inCodeBlock &&
|
|
189
|
+
![currentActiveStyles
|
|
190
|
+
containsObject:@([CodeBlockStyle getStyleType])]) {
|
|
138
191
|
inCodeBlock = NO;
|
|
139
192
|
[result appendString:@"\n</codeblock>"];
|
|
140
193
|
}
|
|
141
|
-
|
|
194
|
+
|
|
142
195
|
// handle starting unordered list
|
|
143
|
-
if(!inUnorderedList &&
|
|
196
|
+
if (!inUnorderedList &&
|
|
197
|
+
[currentActiveStyles
|
|
198
|
+
containsObject:@([UnorderedListStyle getStyleType])]) {
|
|
144
199
|
inUnorderedList = YES;
|
|
145
200
|
[result appendString:@"\n<ul>"];
|
|
146
201
|
}
|
|
147
202
|
// handle starting ordered list
|
|
148
|
-
if(!inOrderedList &&
|
|
203
|
+
if (!inOrderedList &&
|
|
204
|
+
[currentActiveStyles
|
|
205
|
+
containsObject:@([OrderedListStyle getStyleType])]) {
|
|
149
206
|
inOrderedList = YES;
|
|
150
207
|
[result appendString:@"\n<ol>"];
|
|
151
208
|
}
|
|
152
209
|
// handle starting blockquotes
|
|
153
|
-
if(!inBlockQuote &&
|
|
210
|
+
if (!inBlockQuote &&
|
|
211
|
+
[currentActiveStyles
|
|
212
|
+
containsObject:@([BlockQuoteStyle getStyleType])]) {
|
|
154
213
|
inBlockQuote = YES;
|
|
155
214
|
[result appendString:@"\n<blockquote>"];
|
|
156
215
|
}
|
|
157
216
|
// handle starting codeblock
|
|
158
|
-
if(!inCodeBlock &&
|
|
217
|
+
if (!inCodeBlock &&
|
|
218
|
+
[currentActiveStyles
|
|
219
|
+
containsObject:@([CodeBlockStyle getStyleType])]) {
|
|
159
220
|
inCodeBlock = YES;
|
|
160
221
|
[result appendString:@"\n<codeblock>"];
|
|
161
222
|
}
|
|
162
|
-
|
|
223
|
+
|
|
163
224
|
// don't add the <p> tag if some paragraph styles are present
|
|
164
|
-
if([currentActiveStyles
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
225
|
+
if ([currentActiveStyles
|
|
226
|
+
containsObject:@([UnorderedListStyle getStyleType])] ||
|
|
227
|
+
[currentActiveStyles
|
|
228
|
+
containsObject:@([OrderedListStyle getStyleType])] ||
|
|
229
|
+
[currentActiveStyles containsObject:@([H1Style getStyleType])] ||
|
|
230
|
+
[currentActiveStyles containsObject:@([H2Style getStyleType])] ||
|
|
231
|
+
[currentActiveStyles containsObject:@([H3Style getStyleType])] ||
|
|
232
|
+
[currentActiveStyles containsObject:@([H4Style getStyleType])] ||
|
|
233
|
+
[currentActiveStyles containsObject:@([H5Style getStyleType])] ||
|
|
234
|
+
[currentActiveStyles containsObject:@([H6Style getStyleType])] ||
|
|
235
|
+
[currentActiveStyles
|
|
236
|
+
containsObject:@([BlockQuoteStyle getStyleType])] ||
|
|
237
|
+
[currentActiveStyles
|
|
238
|
+
containsObject:@([CodeBlockStyle getStyleType])]) {
|
|
172
239
|
[result appendString:@"\n"];
|
|
173
240
|
} else {
|
|
174
241
|
[result appendString:@"\n<p>"];
|
|
175
242
|
}
|
|
176
243
|
}
|
|
177
|
-
|
|
244
|
+
|
|
178
245
|
// get styles that have ended
|
|
179
|
-
NSMutableSet<NSNumber *> *endedStyles =
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
246
|
+
NSMutableSet<NSNumber *> *endedStyles =
|
|
247
|
+
[previousActiveStyles mutableCopy];
|
|
248
|
+
[endedStyles minusSet:currentActiveStyles];
|
|
249
|
+
|
|
250
|
+
// also finish styles that should be ended becasue they are nested in a
|
|
251
|
+
// style that ended
|
|
183
252
|
NSMutableSet *fixedEndedStyles = [endedStyles mutableCopy];
|
|
184
253
|
NSMutableSet *stylesToBeReAdded = [[NSMutableSet alloc] init];
|
|
185
|
-
|
|
186
|
-
for(NSNumber *style in endedStyles) {
|
|
187
|
-
NSInteger styleBeginning =
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
254
|
+
|
|
255
|
+
for (NSNumber *style in endedStyles) {
|
|
256
|
+
NSInteger styleBeginning =
|
|
257
|
+
[currentActiveStylesBeginning[style] integerValue];
|
|
258
|
+
|
|
259
|
+
for (NSNumber *activeStyle in currentActiveStyles) {
|
|
260
|
+
NSInteger activeStyleBeginning =
|
|
261
|
+
[currentActiveStylesBeginning[activeStyle] integerValue];
|
|
262
|
+
|
|
263
|
+
// we end the styles that began after the currently ended style but
|
|
264
|
+
// not at the "i" (cause the old style ended at exactly "i-1" also the
|
|
265
|
+
// ones that began in the exact same place but are "inner" in relation
|
|
266
|
+
// to them due to StyleTypeEnum integer values
|
|
267
|
+
|
|
268
|
+
if ((activeStyleBeginning > styleBeginning &&
|
|
269
|
+
activeStyleBeginning < i) ||
|
|
270
|
+
(activeStyleBeginning == styleBeginning &&
|
|
271
|
+
activeStyleBeginning<
|
|
272
|
+
i && [activeStyle integerValue]>[style integerValue])) {
|
|
197
273
|
[fixedEndedStyles addObject:activeStyle];
|
|
198
274
|
[stylesToBeReAdded addObject:activeStyle];
|
|
199
275
|
}
|
|
200
276
|
}
|
|
201
277
|
}
|
|
202
|
-
|
|
203
|
-
// if a style begins but there is a style inner to it that is (and was
|
|
204
|
-
|
|
278
|
+
|
|
279
|
+
// if a style begins but there is a style inner to it that is (and was
|
|
280
|
+
// previously) active, it also should be closed and readded
|
|
281
|
+
|
|
205
282
|
// newly added styles
|
|
206
283
|
NSMutableSet *newStyles = [currentActiveStyles mutableCopy];
|
|
207
|
-
[newStyles minusSet:
|
|
284
|
+
[newStyles minusSet:previousActiveStyles];
|
|
208
285
|
// styles that were and still are active
|
|
209
286
|
NSMutableSet *stillActiveStyles = [previousActiveStyles mutableCopy];
|
|
210
287
|
[stillActiveStyles intersectSet:currentActiveStyles];
|
|
211
|
-
|
|
212
|
-
for(NSNumber *style in newStyles) {
|
|
213
|
-
for(NSNumber *ongoingStyle in stillActiveStyles) {
|
|
214
|
-
if([ongoingStyle integerValue] > [style integerValue]) {
|
|
288
|
+
|
|
289
|
+
for (NSNumber *style in newStyles) {
|
|
290
|
+
for (NSNumber *ongoingStyle in stillActiveStyles) {
|
|
291
|
+
if ([ongoingStyle integerValue] > [style integerValue]) {
|
|
215
292
|
// the prev style is inner; needs to be closed and re-added later
|
|
216
293
|
[fixedEndedStyles addObject:ongoingStyle];
|
|
217
294
|
[stylesToBeReAdded addObject:ongoingStyle];
|
|
218
295
|
}
|
|
219
296
|
}
|
|
220
297
|
}
|
|
221
|
-
|
|
298
|
+
|
|
222
299
|
// they are sorted in a descending order
|
|
223
|
-
NSArray<NSNumber*> *sortedEndedStyles = [fixedEndedStyles
|
|
224
|
-
|
|
300
|
+
NSArray<NSNumber *> *sortedEndedStyles = [fixedEndedStyles
|
|
301
|
+
sortedArrayUsingDescriptors:@[ [NSSortDescriptor
|
|
302
|
+
sortDescriptorWithKey:@"intValue"
|
|
303
|
+
ascending:NO] ]];
|
|
304
|
+
|
|
225
305
|
// append closing tags
|
|
226
|
-
for(NSNumber *style in sortedEndedStyles) {
|
|
227
|
-
if ([style isEqualToNumber
|
|
306
|
+
for (NSNumber *style in sortedEndedStyles) {
|
|
307
|
+
if ([style isEqualToNumber:@([ImageStyle getStyleType])]) {
|
|
228
308
|
continue;
|
|
229
309
|
}
|
|
230
|
-
NSString *tagContent = [self tagContentForStyle:style
|
|
231
|
-
|
|
310
|
+
NSString *tagContent = [self tagContentForStyle:style
|
|
311
|
+
openingTag:NO
|
|
312
|
+
location:currentRange.location];
|
|
313
|
+
[result appendString:[NSString stringWithFormat:@"</%@>", tagContent]];
|
|
232
314
|
}
|
|
233
|
-
|
|
234
|
-
// all styles that have begun: new styles + the ones that need to be
|
|
235
|
-
// they are sorted in a ascending manner to properly keep tags'
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
315
|
+
|
|
316
|
+
// all styles that have begun: new styles + the ones that need to be
|
|
317
|
+
// re-added they are sorted in a ascending manner to properly keep tags'
|
|
318
|
+
// FILO order
|
|
319
|
+
[newStyles unionSet:stylesToBeReAdded];
|
|
320
|
+
NSArray<NSNumber *> *sortedNewStyles = [newStyles
|
|
321
|
+
sortedArrayUsingDescriptors:@[ [NSSortDescriptor
|
|
322
|
+
sortDescriptorWithKey:@"intValue"
|
|
323
|
+
ascending:YES] ]];
|
|
324
|
+
|
|
239
325
|
// append opening tags
|
|
240
|
-
for(NSNumber *style in sortedNewStyles) {
|
|
241
|
-
NSString *tagContent = [self tagContentForStyle:style
|
|
242
|
-
|
|
243
|
-
|
|
326
|
+
for (NSNumber *style in sortedNewStyles) {
|
|
327
|
+
NSString *tagContent = [self tagContentForStyle:style
|
|
328
|
+
openingTag:YES
|
|
329
|
+
location:currentRange.location];
|
|
330
|
+
if ([style isEqualToNumber:@([ImageStyle getStyleType])]) {
|
|
331
|
+
[result
|
|
332
|
+
appendString:[NSString stringWithFormat:@"<%@/>", tagContent]];
|
|
244
333
|
[currentActiveStyles removeObject:@([ImageStyle getStyleType])];
|
|
245
334
|
} else {
|
|
246
|
-
[result appendString:
|
|
335
|
+
[result appendString:[NSString stringWithFormat:@"<%@>", tagContent]];
|
|
247
336
|
}
|
|
248
337
|
}
|
|
249
|
-
|
|
338
|
+
|
|
250
339
|
// append the letter and escape it if needed
|
|
251
|
-
[result appendString:
|
|
252
|
-
|
|
340
|
+
[result appendString:[NSString stringByEscapingHtml:currentCharacterStr]];
|
|
341
|
+
|
|
253
342
|
// save current styles for next character's checks
|
|
254
343
|
previousActiveStyles = currentActiveStyles;
|
|
255
344
|
}
|
|
256
|
-
|
|
345
|
+
|
|
257
346
|
// set last character
|
|
258
347
|
lastCharacter = currentCharacterChar;
|
|
259
348
|
}
|
|
260
|
-
|
|
261
|
-
if(![[NSCharacterSet newlineCharacterSet] characterIsMember:lastCharacter]) {
|
|
349
|
+
|
|
350
|
+
if (![[NSCharacterSet newlineCharacterSet] characterIsMember:lastCharacter]) {
|
|
262
351
|
// not-newline character was last - finish the paragraph
|
|
263
352
|
// close all pending tags
|
|
264
|
-
NSArray<NSNumber*> *sortedEndedStyles = [previousActiveStyles
|
|
265
|
-
|
|
353
|
+
NSArray<NSNumber *> *sortedEndedStyles = [previousActiveStyles
|
|
354
|
+
sortedArrayUsingDescriptors:@[ [NSSortDescriptor
|
|
355
|
+
sortDescriptorWithKey:@"intValue"
|
|
356
|
+
ascending:NO] ]];
|
|
357
|
+
|
|
266
358
|
// append closing tags
|
|
267
|
-
for(NSNumber *style in sortedEndedStyles) {
|
|
268
|
-
if ([style isEqualToNumber
|
|
359
|
+
for (NSNumber *style in sortedEndedStyles) {
|
|
360
|
+
if ([style isEqualToNumber:@([ImageStyle getStyleType])]) {
|
|
269
361
|
continue;
|
|
270
362
|
}
|
|
271
|
-
NSString *tagContent = [self
|
|
272
|
-
|
|
363
|
+
NSString *tagContent = [self
|
|
364
|
+
tagContentForStyle:style
|
|
365
|
+
openingTag:NO
|
|
366
|
+
location:_input->textView.textStorage.string.length - 1];
|
|
367
|
+
[result appendString:[NSString stringWithFormat:@"</%@>", tagContent]];
|
|
273
368
|
}
|
|
274
|
-
|
|
369
|
+
|
|
275
370
|
// finish the paragraph
|
|
276
371
|
// handle ending of some paragraph styles
|
|
277
|
-
if([previousActiveStyles
|
|
372
|
+
if ([previousActiveStyles
|
|
373
|
+
containsObject:@([UnorderedListStyle getStyleType])]) {
|
|
278
374
|
[result appendString:@"\n</ul>"];
|
|
279
|
-
} else if([previousActiveStyles
|
|
375
|
+
} else if ([previousActiveStyles
|
|
376
|
+
containsObject:@([OrderedListStyle getStyleType])]) {
|
|
280
377
|
[result appendString:@"\n</ol>"];
|
|
281
|
-
} else if([previousActiveStyles
|
|
378
|
+
} else if ([previousActiveStyles
|
|
379
|
+
containsObject:@([BlockQuoteStyle getStyleType])]) {
|
|
282
380
|
[result appendString:@"\n</blockquote>"];
|
|
283
|
-
} else if([previousActiveStyles
|
|
381
|
+
} else if ([previousActiveStyles
|
|
382
|
+
containsObject:@([CodeBlockStyle getStyleType])]) {
|
|
284
383
|
[result appendString:@"\n</codeblock>"];
|
|
285
|
-
} else if(
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
384
|
+
} else if ([previousActiveStyles
|
|
385
|
+
containsObject:@([H1Style getStyleType])] ||
|
|
386
|
+
[previousActiveStyles
|
|
387
|
+
containsObject:@([H2Style getStyleType])] ||
|
|
388
|
+
[previousActiveStyles
|
|
389
|
+
containsObject:@([H3Style getStyleType])] ||
|
|
390
|
+
[previousActiveStyles
|
|
391
|
+
containsObject:@([H4Style getStyleType])] ||
|
|
392
|
+
[previousActiveStyles
|
|
393
|
+
containsObject:@([H5Style getStyleType])] ||
|
|
394
|
+
[previousActiveStyles
|
|
395
|
+
containsObject:@([H6Style getStyleType])]) {
|
|
396
|
+
// do nothing, heading closing tag has already been appended
|
|
291
397
|
} else {
|
|
292
398
|
[result appendString:@"</p>"];
|
|
293
399
|
}
|
|
294
400
|
} else {
|
|
295
401
|
// newline character was last - some paragraph styles need to be closed
|
|
296
|
-
if(inUnorderedList) {
|
|
402
|
+
if (inUnorderedList) {
|
|
297
403
|
inUnorderedList = NO;
|
|
298
404
|
[result appendString:@"\n</ul>"];
|
|
299
405
|
}
|
|
300
|
-
if(inOrderedList) {
|
|
406
|
+
if (inOrderedList) {
|
|
301
407
|
inOrderedList = NO;
|
|
302
408
|
[result appendString:@"\n</ol>"];
|
|
303
409
|
}
|
|
304
|
-
if(inBlockQuote) {
|
|
410
|
+
if (inBlockQuote) {
|
|
305
411
|
inBlockQuote = NO;
|
|
306
412
|
[result appendString:@"\n</blockquote>"];
|
|
307
413
|
}
|
|
308
|
-
if(inCodeBlock) {
|
|
414
|
+
if (inCodeBlock) {
|
|
309
415
|
inCodeBlock = NO;
|
|
310
416
|
[result appendString:@"\n</codeblock>"];
|
|
311
417
|
}
|
|
312
418
|
}
|
|
313
|
-
|
|
314
|
-
[result appendString
|
|
315
|
-
|
|
419
|
+
|
|
420
|
+
[result appendString:@"\n</html>"];
|
|
421
|
+
|
|
422
|
+
// remove Object Replacement Characters in the very end
|
|
423
|
+
[result replaceOccurrencesOfString:@"\uFFFC"
|
|
424
|
+
withString:@""
|
|
425
|
+
options:0
|
|
426
|
+
range:NSMakeRange(0, result.length)];
|
|
427
|
+
|
|
316
428
|
// remove zero width spaces in the very end
|
|
317
|
-
|
|
318
|
-
|
|
429
|
+
[result replaceOccurrencesOfString:@"\u200B"
|
|
430
|
+
withString:@""
|
|
431
|
+
options:0
|
|
432
|
+
range:NSMakeRange(0, result.length)];
|
|
433
|
+
|
|
434
|
+
// replace empty <p></p> into <br> in the very end
|
|
435
|
+
[result replaceOccurrencesOfString:@"<p></p>"
|
|
436
|
+
withString:@"<br>"
|
|
437
|
+
options:0
|
|
438
|
+
range:NSMakeRange(0, result.length)];
|
|
439
|
+
|
|
319
440
|
return result;
|
|
320
441
|
}
|
|
321
442
|
|
|
322
|
-
- (NSString *)tagContentForStyle:(NSNumber *)style
|
|
323
|
-
|
|
443
|
+
- (NSString *)tagContentForStyle:(NSNumber *)style
|
|
444
|
+
openingTag:(BOOL)openingTag
|
|
445
|
+
location:(NSInteger)location {
|
|
446
|
+
if ([style isEqualToNumber:@([BoldStyle getStyleType])]) {
|
|
324
447
|
return @"b";
|
|
325
|
-
} else if([style isEqualToNumber
|
|
448
|
+
} else if ([style isEqualToNumber:@([ItalicStyle getStyleType])]) {
|
|
326
449
|
return @"i";
|
|
327
|
-
} else if ([style isEqualToNumber
|
|
328
|
-
if(openingTag) {
|
|
329
|
-
ImageStyle *imageStyle =
|
|
330
|
-
|
|
450
|
+
} else if ([style isEqualToNumber:@([ImageStyle getStyleType])]) {
|
|
451
|
+
if (openingTag) {
|
|
452
|
+
ImageStyle *imageStyle =
|
|
453
|
+
(ImageStyle *)_input->stylesDict[@([ImageStyle getStyleType])];
|
|
454
|
+
if (imageStyle != nullptr) {
|
|
331
455
|
ImageData *data = [imageStyle getImageDataAt:location];
|
|
332
|
-
if(data != nullptr && data.uri != nullptr) {
|
|
333
|
-
return [NSString
|
|
456
|
+
if (data != nullptr && data.uri != nullptr) {
|
|
457
|
+
return [NSString
|
|
458
|
+
stringWithFormat:@"img src=\"%@\" width=\"%f\" height=\"%f\"",
|
|
459
|
+
data.uri, data.width, data.height];
|
|
334
460
|
}
|
|
335
461
|
}
|
|
336
462
|
return @"img";
|
|
337
463
|
} else {
|
|
338
464
|
return @"";
|
|
339
465
|
}
|
|
340
|
-
} else if([style isEqualToNumber
|
|
466
|
+
} else if ([style isEqualToNumber:@([UnderlineStyle getStyleType])]) {
|
|
341
467
|
return @"u";
|
|
342
|
-
} else if([style isEqualToNumber
|
|
468
|
+
} else if ([style isEqualToNumber:@([StrikethroughStyle getStyleType])]) {
|
|
343
469
|
return @"s";
|
|
344
|
-
} else if([style isEqualToNumber
|
|
470
|
+
} else if ([style isEqualToNumber:@([InlineCodeStyle getStyleType])]) {
|
|
345
471
|
return @"code";
|
|
346
|
-
} else if([style isEqualToNumber
|
|
347
|
-
if(openingTag) {
|
|
348
|
-
LinkStyle *linkStyle =
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
472
|
+
} else if ([style isEqualToNumber:@([LinkStyle getStyleType])]) {
|
|
473
|
+
if (openingTag) {
|
|
474
|
+
LinkStyle *linkStyle =
|
|
475
|
+
(LinkStyle *)_input->stylesDict[@([LinkStyle getStyleType])];
|
|
476
|
+
if (linkStyle != nullptr) {
|
|
477
|
+
LinkData *data = [linkStyle getLinkDataAt:location];
|
|
478
|
+
if (data != nullptr && data.url != nullptr) {
|
|
352
479
|
return [NSString stringWithFormat:@"a href=\"%@\"", data.url];
|
|
353
480
|
}
|
|
354
481
|
}
|
|
@@ -356,293 +483,606 @@
|
|
|
356
483
|
} else {
|
|
357
484
|
return @"a";
|
|
358
485
|
}
|
|
359
|
-
} else if([style isEqualToNumber
|
|
360
|
-
if(openingTag) {
|
|
361
|
-
MentionStyle *mentionStyle =
|
|
362
|
-
|
|
486
|
+
} else if ([style isEqualToNumber:@([MentionStyle getStyleType])]) {
|
|
487
|
+
if (openingTag) {
|
|
488
|
+
MentionStyle *mentionStyle =
|
|
489
|
+
(MentionStyle *)_input->stylesDict[@([MentionStyle getStyleType])];
|
|
490
|
+
if (mentionStyle != nullptr) {
|
|
363
491
|
MentionParams *params = [mentionStyle getMentionParamsAt:location];
|
|
364
492
|
// attributes can theoretically be nullptr
|
|
365
|
-
if(params != nullptr && params.indicator != nullptr &&
|
|
366
|
-
|
|
367
|
-
|
|
493
|
+
if (params != nullptr && params.indicator != nullptr &&
|
|
494
|
+
params.text != nullptr) {
|
|
495
|
+
NSMutableString *attrsStr =
|
|
496
|
+
[[NSMutableString alloc] initWithString:@""];
|
|
497
|
+
if (params.attributes != nullptr) {
|
|
368
498
|
// turn attributes to Data and then into dict
|
|
369
|
-
NSData *attrsData =
|
|
499
|
+
NSData *attrsData =
|
|
500
|
+
[params.attributes dataUsingEncoding:NSUTF8StringEncoding];
|
|
370
501
|
NSError *jsonError;
|
|
371
|
-
NSDictionary *json =
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
502
|
+
NSDictionary *json =
|
|
503
|
+
[NSJSONSerialization JSONObjectWithData:attrsData
|
|
504
|
+
options:0
|
|
505
|
+
error:&jsonError];
|
|
375
506
|
// format dict keys and values into string
|
|
376
|
-
[json enumerateKeysAndObjectsUsingBlock:^(
|
|
377
|
-
|
|
507
|
+
[json enumerateKeysAndObjectsUsingBlock:^(
|
|
508
|
+
id _Nonnull key, id _Nonnull obj, BOOL *_Nonnull stop) {
|
|
509
|
+
[attrsStr
|
|
510
|
+
appendString:[NSString stringWithFormat:@" %@=\"%@\"",
|
|
511
|
+
(NSString *)key,
|
|
512
|
+
(NSString *)obj]];
|
|
378
513
|
}];
|
|
379
514
|
}
|
|
380
|
-
return [NSString
|
|
515
|
+
return [NSString
|
|
516
|
+
stringWithFormat:@"mention text=\"%@\" indicator=\"%@\"%@",
|
|
517
|
+
params.text, params.indicator, attrsStr];
|
|
381
518
|
}
|
|
382
519
|
}
|
|
383
520
|
return @"mention";
|
|
384
521
|
} else {
|
|
385
522
|
return @"mention";
|
|
386
523
|
}
|
|
387
|
-
} else if([style isEqualToNumber:@([H1Style getStyleType])]) {
|
|
524
|
+
} else if ([style isEqualToNumber:@([H1Style getStyleType])]) {
|
|
388
525
|
return @"h1";
|
|
389
|
-
} else if([style isEqualToNumber:@([H2Style getStyleType])]) {
|
|
526
|
+
} else if ([style isEqualToNumber:@([H2Style getStyleType])]) {
|
|
390
527
|
return @"h2";
|
|
391
|
-
} else if([style isEqualToNumber:@([H3Style getStyleType])]) {
|
|
528
|
+
} else if ([style isEqualToNumber:@([H3Style getStyleType])]) {
|
|
392
529
|
return @"h3";
|
|
393
|
-
} else if
|
|
530
|
+
} else if ([style isEqualToNumber:@([H4Style getStyleType])]) {
|
|
531
|
+
return @"h4";
|
|
532
|
+
} else if ([style isEqualToNumber:@([H5Style getStyleType])]) {
|
|
533
|
+
return @"h5";
|
|
534
|
+
} else if ([style isEqualToNumber:@([H6Style getStyleType])]) {
|
|
535
|
+
return @"h6";
|
|
536
|
+
} else if ([style isEqualToNumber:@([UnorderedListStyle getStyleType])] ||
|
|
537
|
+
[style isEqualToNumber:@([OrderedListStyle getStyleType])]) {
|
|
394
538
|
return @"li";
|
|
395
|
-
} else if([style isEqualToNumber:@([BlockQuoteStyle getStyleType])] ||
|
|
539
|
+
} else if ([style isEqualToNumber:@([BlockQuoteStyle getStyleType])] ||
|
|
540
|
+
[style isEqualToNumber:@([CodeBlockStyle getStyleType])]) {
|
|
396
541
|
// blockquotes and codeblock use <p> tags the same way lists use <li>
|
|
397
542
|
return @"p";
|
|
398
543
|
}
|
|
399
544
|
return @"";
|
|
400
545
|
}
|
|
401
546
|
|
|
402
|
-
- (void)replaceWholeFromHtml:(NSString *
|
|
547
|
+
- (void)replaceWholeFromHtml:(NSString *_Nonnull)html {
|
|
403
548
|
NSArray *processingResult = [self getTextAndStylesFromHtml:html];
|
|
404
549
|
NSString *plainText = (NSString *)processingResult[0];
|
|
405
550
|
NSArray *stylesInfo = (NSArray *)processingResult[1];
|
|
406
|
-
|
|
551
|
+
|
|
407
552
|
// reset the text first and reset typing attributes
|
|
408
553
|
_input->textView.text = @"";
|
|
409
554
|
_input->textView.typingAttributes = _input->defaultTypingAttributes;
|
|
410
|
-
|
|
555
|
+
|
|
411
556
|
// set new text
|
|
412
557
|
_input->textView.text = plainText;
|
|
413
|
-
|
|
558
|
+
|
|
414
559
|
// re-apply the styles
|
|
415
|
-
[self applyProcessedStyles:stylesInfo
|
|
560
|
+
[self applyProcessedStyles:stylesInfo
|
|
561
|
+
offsetFromBeginning:0
|
|
562
|
+
plainTextLength:plainText.length];
|
|
416
563
|
}
|
|
417
564
|
|
|
418
|
-
- (void)replaceFromHtml:(NSString *
|
|
565
|
+
- (void)replaceFromHtml:(NSString *_Nonnull)html range:(NSRange)range {
|
|
419
566
|
NSArray *processingResult = [self getTextAndStylesFromHtml:html];
|
|
420
567
|
NSString *plainText = (NSString *)processingResult[0];
|
|
421
568
|
NSArray *stylesInfo = (NSArray *)processingResult[1];
|
|
422
|
-
|
|
569
|
+
|
|
423
570
|
// we can use ready replace util
|
|
424
|
-
[TextInsertionUtils replaceText:plainText
|
|
425
|
-
|
|
426
|
-
|
|
571
|
+
[TextInsertionUtils replaceText:plainText
|
|
572
|
+
at:range
|
|
573
|
+
additionalAttributes:nil
|
|
574
|
+
input:_input
|
|
575
|
+
withSelection:YES];
|
|
576
|
+
|
|
577
|
+
[self applyProcessedStyles:stylesInfo
|
|
578
|
+
offsetFromBeginning:range.location
|
|
579
|
+
plainTextLength:plainText.length];
|
|
427
580
|
}
|
|
428
581
|
|
|
429
|
-
- (void)insertFromHtml:(NSString *
|
|
582
|
+
- (void)insertFromHtml:(NSString *_Nonnull)html location:(NSInteger)location {
|
|
430
583
|
NSArray *processingResult = [self getTextAndStylesFromHtml:html];
|
|
431
584
|
NSString *plainText = (NSString *)processingResult[0];
|
|
432
585
|
NSArray *stylesInfo = (NSArray *)processingResult[1];
|
|
433
|
-
|
|
586
|
+
|
|
434
587
|
// same here, insertion utils got our back
|
|
435
|
-
[TextInsertionUtils insertText:plainText
|
|
436
|
-
|
|
437
|
-
|
|
588
|
+
[TextInsertionUtils insertText:plainText
|
|
589
|
+
at:location
|
|
590
|
+
additionalAttributes:nil
|
|
591
|
+
input:_input
|
|
592
|
+
withSelection:YES];
|
|
593
|
+
|
|
594
|
+
[self applyProcessedStyles:stylesInfo
|
|
595
|
+
offsetFromBeginning:location
|
|
596
|
+
plainTextLength:plainText.length];
|
|
438
597
|
}
|
|
439
598
|
|
|
440
|
-
- (void)applyProcessedStyles:(NSArray *)processedStyles
|
|
441
|
-
|
|
599
|
+
- (void)applyProcessedStyles:(NSArray *)processedStyles
|
|
600
|
+
offsetFromBeginning:(NSInteger)offset
|
|
601
|
+
plainTextLength:(NSUInteger)plainTextLength {
|
|
602
|
+
for (NSArray *arr in processedStyles) {
|
|
442
603
|
// unwrap all info from processed style
|
|
443
604
|
NSNumber *styleType = (NSNumber *)arr[0];
|
|
444
605
|
StylePair *stylePair = (StylePair *)arr[1];
|
|
445
606
|
id<BaseStyleProtocol> baseStyle = _input->stylesDict[styleType];
|
|
446
|
-
// range must be taking offest into consideration because processed styles'
|
|
447
|
-
//
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
607
|
+
// range must be taking offest into consideration because processed styles'
|
|
608
|
+
// ranges are relative to only the new text while we need absolute ranges
|
|
609
|
+
// relative to the whole existing text
|
|
610
|
+
NSRange styleRange =
|
|
611
|
+
NSMakeRange(offset + [stylePair.rangeValue rangeValue].location,
|
|
612
|
+
[stylePair.rangeValue rangeValue].length);
|
|
613
|
+
|
|
614
|
+
// of course any changes here need to take blocks and conflicts into
|
|
615
|
+
// consideration
|
|
616
|
+
if ([_input handleStyleBlocksAndConflicts:[[baseStyle class] getStyleType]
|
|
617
|
+
range:styleRange]) {
|
|
618
|
+
if ([styleType isEqualToNumber:@([LinkStyle getStyleType])]) {
|
|
619
|
+
NSString *text =
|
|
620
|
+
[_input->textView.textStorage.string substringWithRange:styleRange];
|
|
454
621
|
NSString *url = (NSString *)stylePair.styleValue;
|
|
455
622
|
BOOL isManual = ![text isEqualToString:url];
|
|
456
|
-
[((LinkStyle *)baseStyle) addLink:text
|
|
457
|
-
|
|
623
|
+
[((LinkStyle *)baseStyle) addLink:text
|
|
624
|
+
url:url
|
|
625
|
+
range:styleRange
|
|
626
|
+
manual:isManual
|
|
627
|
+
withSelection:NO];
|
|
628
|
+
} else if ([styleType isEqualToNumber:@([MentionStyle getStyleType])]) {
|
|
458
629
|
MentionParams *params = (MentionParams *)stylePair.styleValue;
|
|
459
|
-
[((MentionStyle *)baseStyle) addMentionAtRange:styleRange
|
|
460
|
-
|
|
630
|
+
[((MentionStyle *)baseStyle) addMentionAtRange:styleRange
|
|
631
|
+
params:params];
|
|
632
|
+
} else if ([styleType isEqualToNumber:@([ImageStyle getStyleType])]) {
|
|
461
633
|
ImageData *imgData = (ImageData *)stylePair.styleValue;
|
|
462
|
-
[((ImageStyle *)baseStyle) addImageAtRange:styleRange
|
|
634
|
+
[((ImageStyle *)baseStyle) addImageAtRange:styleRange
|
|
635
|
+
imageData:imgData
|
|
636
|
+
withSelection:NO];
|
|
463
637
|
} else {
|
|
464
|
-
|
|
638
|
+
BOOL shouldAddTypingAttr =
|
|
639
|
+
styleRange.location + styleRange.length == plainTextLength;
|
|
640
|
+
[baseStyle addAttributes:styleRange withTypingAttr:shouldAddTypingAttr];
|
|
465
641
|
}
|
|
466
642
|
}
|
|
467
643
|
}
|
|
468
644
|
[_input anyTextMayHaveBeenModified];
|
|
469
645
|
}
|
|
470
646
|
|
|
471
|
-
- (NSString *
|
|
647
|
+
- (NSString *_Nullable)initiallyProcessHtml:(NSString *_Nonnull)html {
|
|
648
|
+
NSString *htmlWithoutSpaces = [self stripExtraWhiteSpacesAndNewlines:html];
|
|
472
649
|
NSString *fixedHtml = nullptr;
|
|
473
|
-
|
|
474
|
-
if(
|
|
475
|
-
NSString *firstSix =
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
650
|
+
|
|
651
|
+
if (htmlWithoutSpaces.length >= 13) {
|
|
652
|
+
NSString *firstSix =
|
|
653
|
+
[htmlWithoutSpaces substringWithRange:NSMakeRange(0, 6)];
|
|
654
|
+
NSString *lastSeven = [htmlWithoutSpaces
|
|
655
|
+
substringWithRange:NSMakeRange(htmlWithoutSpaces.length - 7, 7)];
|
|
656
|
+
|
|
657
|
+
if ([firstSix isEqualToString:@"<html>"] &&
|
|
658
|
+
[lastSeven isEqualToString:@"</html>"]) {
|
|
479
659
|
// remove html tags, might be with newlines or without them
|
|
480
|
-
fixedHtml = [
|
|
660
|
+
fixedHtml = [htmlWithoutSpaces copy];
|
|
481
661
|
// firstly remove newlined html tags if any:
|
|
482
|
-
fixedHtml = [fixedHtml stringByReplacingOccurrencesOfString:@"<html>\n"
|
|
483
|
-
|
|
662
|
+
fixedHtml = [fixedHtml stringByReplacingOccurrencesOfString:@"<html>\n"
|
|
663
|
+
withString:@""];
|
|
664
|
+
fixedHtml = [fixedHtml stringByReplacingOccurrencesOfString:@"\n</html>"
|
|
665
|
+
withString:@""];
|
|
484
666
|
// fallback; remove html tags without their newlines
|
|
485
|
-
fixedHtml = [fixedHtml stringByReplacingOccurrencesOfString:@"<html>"
|
|
486
|
-
|
|
667
|
+
fixedHtml = [fixedHtml stringByReplacingOccurrencesOfString:@"<html>"
|
|
668
|
+
withString:@""];
|
|
669
|
+
fixedHtml = [fixedHtml stringByReplacingOccurrencesOfString:@"</html>"
|
|
670
|
+
withString:@""];
|
|
487
671
|
} else {
|
|
488
|
-
// in other case we are most likely working with some external html - try
|
|
489
|
-
|
|
490
|
-
NSRange
|
|
491
|
-
|
|
492
|
-
|
|
672
|
+
// in other case we are most likely working with some external html - try
|
|
673
|
+
// getting the styles from between body tags
|
|
674
|
+
NSRange openingBodyRange = [htmlWithoutSpaces rangeOfString:@"<body>"];
|
|
675
|
+
NSRange closingBodyRange = [htmlWithoutSpaces rangeOfString:@"</body>"];
|
|
676
|
+
|
|
677
|
+
if (openingBodyRange.length != 0 && closingBodyRange.length != 0) {
|
|
493
678
|
NSInteger newStart = openingBodyRange.location + 7;
|
|
494
679
|
NSInteger newEnd = closingBodyRange.location - 1;
|
|
495
|
-
fixedHtml = [
|
|
680
|
+
fixedHtml = [htmlWithoutSpaces
|
|
681
|
+
substringWithRange:NSMakeRange(newStart, newEnd - newStart + 1)];
|
|
496
682
|
}
|
|
497
683
|
}
|
|
498
684
|
}
|
|
499
|
-
|
|
685
|
+
|
|
500
686
|
// second processing - try fixing htmls with wrong newlines' setup
|
|
501
|
-
if(fixedHtml != nullptr) {
|
|
687
|
+
if (fixedHtml != nullptr) {
|
|
502
688
|
// add <br> tag wherever needed
|
|
503
|
-
fixedHtml = [fixedHtml stringByReplacingOccurrencesOfString:@"<p></p>"
|
|
504
|
-
|
|
689
|
+
fixedHtml = [fixedHtml stringByReplacingOccurrencesOfString:@"<p></p>"
|
|
690
|
+
withString:@"<br>"];
|
|
691
|
+
|
|
505
692
|
// remove <p> tags inside of <li>
|
|
506
|
-
fixedHtml = [fixedHtml stringByReplacingOccurrencesOfString:@"<li><p>"
|
|
507
|
-
|
|
508
|
-
|
|
693
|
+
fixedHtml = [fixedHtml stringByReplacingOccurrencesOfString:@"<li><p>"
|
|
694
|
+
withString:@"<li>"];
|
|
695
|
+
fixedHtml = [fixedHtml stringByReplacingOccurrencesOfString:@"</p></li>"
|
|
696
|
+
withString:@"</li>"];
|
|
697
|
+
|
|
698
|
+
// change <br/> to <br>
|
|
699
|
+
fixedHtml = [fixedHtml stringByReplacingOccurrencesOfString:@"<br/>"
|
|
700
|
+
withString:@"<br>"];
|
|
701
|
+
|
|
702
|
+
// remove <p> tags around <br>
|
|
703
|
+
fixedHtml = [fixedHtml stringByReplacingOccurrencesOfString:@"<p><br>"
|
|
704
|
+
withString:@"<br>"];
|
|
705
|
+
fixedHtml = [fixedHtml stringByReplacingOccurrencesOfString:@"<br></p>"
|
|
706
|
+
withString:@"<br>"];
|
|
707
|
+
|
|
509
708
|
// tags that have to be in separate lines
|
|
510
|
-
fixedHtml = [self stringByAddingNewlinesToTag:@"<br>"
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
fixedHtml = [self stringByAddingNewlinesToTag:@"
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
fixedHtml = [self stringByAddingNewlinesToTag:@"</
|
|
519
|
-
|
|
709
|
+
fixedHtml = [self stringByAddingNewlinesToTag:@"<br>"
|
|
710
|
+
inString:fixedHtml
|
|
711
|
+
leading:YES
|
|
712
|
+
trailing:YES];
|
|
713
|
+
fixedHtml = [self stringByAddingNewlinesToTag:@"<ul>"
|
|
714
|
+
inString:fixedHtml
|
|
715
|
+
leading:YES
|
|
716
|
+
trailing:YES];
|
|
717
|
+
fixedHtml = [self stringByAddingNewlinesToTag:@"</ul>"
|
|
718
|
+
inString:fixedHtml
|
|
719
|
+
leading:YES
|
|
720
|
+
trailing:YES];
|
|
721
|
+
fixedHtml = [self stringByAddingNewlinesToTag:@"<ol>"
|
|
722
|
+
inString:fixedHtml
|
|
723
|
+
leading:YES
|
|
724
|
+
trailing:YES];
|
|
725
|
+
fixedHtml = [self stringByAddingNewlinesToTag:@"</ol>"
|
|
726
|
+
inString:fixedHtml
|
|
727
|
+
leading:YES
|
|
728
|
+
trailing:YES];
|
|
729
|
+
fixedHtml = [self stringByAddingNewlinesToTag:@"<blockquote>"
|
|
730
|
+
inString:fixedHtml
|
|
731
|
+
leading:YES
|
|
732
|
+
trailing:YES];
|
|
733
|
+
fixedHtml = [self stringByAddingNewlinesToTag:@"</blockquote>"
|
|
734
|
+
inString:fixedHtml
|
|
735
|
+
leading:YES
|
|
736
|
+
trailing:YES];
|
|
737
|
+
fixedHtml = [self stringByAddingNewlinesToTag:@"<codeblock>"
|
|
738
|
+
inString:fixedHtml
|
|
739
|
+
leading:YES
|
|
740
|
+
trailing:YES];
|
|
741
|
+
fixedHtml = [self stringByAddingNewlinesToTag:@"</codeblock>"
|
|
742
|
+
inString:fixedHtml
|
|
743
|
+
leading:YES
|
|
744
|
+
trailing:YES];
|
|
745
|
+
|
|
520
746
|
// line opening tags
|
|
521
|
-
fixedHtml = [self stringByAddingNewlinesToTag:@"<p>"
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
fixedHtml = [self stringByAddingNewlinesToTag:@"<
|
|
526
|
-
|
|
747
|
+
fixedHtml = [self stringByAddingNewlinesToTag:@"<p>"
|
|
748
|
+
inString:fixedHtml
|
|
749
|
+
leading:YES
|
|
750
|
+
trailing:NO];
|
|
751
|
+
fixedHtml = [self stringByAddingNewlinesToTag:@"<li>"
|
|
752
|
+
inString:fixedHtml
|
|
753
|
+
leading:YES
|
|
754
|
+
trailing:NO];
|
|
755
|
+
fixedHtml = [self stringByAddingNewlinesToTag:@"<h1>"
|
|
756
|
+
inString:fixedHtml
|
|
757
|
+
leading:YES
|
|
758
|
+
trailing:NO];
|
|
759
|
+
fixedHtml = [self stringByAddingNewlinesToTag:@"<h2>"
|
|
760
|
+
inString:fixedHtml
|
|
761
|
+
leading:YES
|
|
762
|
+
trailing:NO];
|
|
763
|
+
fixedHtml = [self stringByAddingNewlinesToTag:@"<h3>"
|
|
764
|
+
inString:fixedHtml
|
|
765
|
+
leading:YES
|
|
766
|
+
trailing:NO];
|
|
767
|
+
fixedHtml = [self stringByAddingNewlinesToTag:@"<h4>"
|
|
768
|
+
inString:fixedHtml
|
|
769
|
+
leading:YES
|
|
770
|
+
trailing:NO];
|
|
771
|
+
fixedHtml = [self stringByAddingNewlinesToTag:@"<h5>"
|
|
772
|
+
inString:fixedHtml
|
|
773
|
+
leading:YES
|
|
774
|
+
trailing:NO];
|
|
775
|
+
fixedHtml = [self stringByAddingNewlinesToTag:@"<h6>"
|
|
776
|
+
inString:fixedHtml
|
|
777
|
+
leading:YES
|
|
778
|
+
trailing:NO];
|
|
779
|
+
|
|
527
780
|
// line closing tags
|
|
528
|
-
fixedHtml = [self stringByAddingNewlinesToTag:@"</p>"
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
fixedHtml = [self stringByAddingNewlinesToTag:@"</
|
|
781
|
+
fixedHtml = [self stringByAddingNewlinesToTag:@"</p>"
|
|
782
|
+
inString:fixedHtml
|
|
783
|
+
leading:NO
|
|
784
|
+
trailing:YES];
|
|
785
|
+
fixedHtml = [self stringByAddingNewlinesToTag:@"</li>"
|
|
786
|
+
inString:fixedHtml
|
|
787
|
+
leading:NO
|
|
788
|
+
trailing:YES];
|
|
789
|
+
fixedHtml = [self stringByAddingNewlinesToTag:@"</h1>"
|
|
790
|
+
inString:fixedHtml
|
|
791
|
+
leading:NO
|
|
792
|
+
trailing:YES];
|
|
793
|
+
fixedHtml = [self stringByAddingNewlinesToTag:@"</h2>"
|
|
794
|
+
inString:fixedHtml
|
|
795
|
+
leading:NO
|
|
796
|
+
trailing:YES];
|
|
797
|
+
fixedHtml = [self stringByAddingNewlinesToTag:@"</h3>"
|
|
798
|
+
inString:fixedHtml
|
|
799
|
+
leading:NO
|
|
800
|
+
trailing:YES];
|
|
801
|
+
fixedHtml = [self stringByAddingNewlinesToTag:@"</h4>"
|
|
802
|
+
inString:fixedHtml
|
|
803
|
+
leading:NO
|
|
804
|
+
trailing:YES];
|
|
805
|
+
fixedHtml = [self stringByAddingNewlinesToTag:@"</h5>"
|
|
806
|
+
inString:fixedHtml
|
|
807
|
+
leading:NO
|
|
808
|
+
trailing:YES];
|
|
809
|
+
fixedHtml = [self stringByAddingNewlinesToTag:@"</h6>"
|
|
810
|
+
inString:fixedHtml
|
|
811
|
+
leading:NO
|
|
812
|
+
trailing:YES];
|
|
813
|
+
|
|
814
|
+
// this is more like a hack but for some reason the last <br> in
|
|
815
|
+
// <blockquote> and <codeblock> are not properly changed into zero width
|
|
816
|
+
// space so we do that manually here
|
|
817
|
+
fixedHtml = [fixedHtml
|
|
818
|
+
stringByReplacingOccurrencesOfString:@"<br>\n</blockquote>"
|
|
819
|
+
withString:@"<p>\u200B</p>\n</blockquote>"];
|
|
820
|
+
fixedHtml = [fixedHtml
|
|
821
|
+
stringByReplacingOccurrencesOfString:@"<br>\n</codeblock>"
|
|
822
|
+
withString:@"<p>\u200B</p>\n</codeblock>"];
|
|
823
|
+
|
|
824
|
+
// replace "<br>" at the end with "<br>\n" if input is not empty to properly
|
|
825
|
+
// handle last <br> in html
|
|
826
|
+
if ([fixedHtml hasSuffix:@"<br>"] && fixedHtml.length != 4) {
|
|
827
|
+
fixedHtml = [fixedHtml stringByAppendingString:@"\n"];
|
|
828
|
+
}
|
|
533
829
|
}
|
|
534
|
-
|
|
830
|
+
|
|
535
831
|
return fixedHtml;
|
|
536
832
|
}
|
|
537
833
|
|
|
538
|
-
|
|
834
|
+
/**
|
|
835
|
+
* Prepares HTML for the parser by stripping extraneous whitespace and newlines
|
|
836
|
+
* from structural tags, while preserving them within text content.
|
|
837
|
+
*
|
|
838
|
+
* APPROACH:
|
|
839
|
+
* This function treats the HTML as having two distinct states:
|
|
840
|
+
* 1. Structure Mode (Depth == 0): We are inside or between container tags (like
|
|
841
|
+
* blockquote, ul, codeblock). In this mode whitespace and newlines are
|
|
842
|
+
* considered layout artifacts and are REMOVED to prevent the parser from
|
|
843
|
+
* creating unwanted spaces.
|
|
844
|
+
* 2. Content Mode (Depth > 0): We are inside a text-containing tag (like p,
|
|
845
|
+
* b, li). In this mode, all whitespace is PRESERVED exactly as is, ensuring
|
|
846
|
+
* that sentences and inline formatting remain readable.
|
|
847
|
+
*
|
|
848
|
+
* The function iterates character-by-character, using a depth counter to track
|
|
849
|
+
* nesting levels of the specific tags defined in `textTags`.
|
|
850
|
+
*
|
|
851
|
+
* IMPORTANT:
|
|
852
|
+
* The `textTags` set acts as a whitelist for "Content Mode". If you add support
|
|
853
|
+
* for a new HTML tag that contains visible text (e.g., h4, h5, h6),
|
|
854
|
+
* you MUST add it to the `textTags` set below.
|
|
855
|
+
*/
|
|
856
|
+
- (NSString *)stripExtraWhiteSpacesAndNewlines:(NSString *)html {
|
|
857
|
+
NSSet *textTags = [NSSet setWithObjects:@"p", @"h1", @"h2", @"h3", @"h4",
|
|
858
|
+
@"h5", @"h6", @"li", @"b", @"a", @"s",
|
|
859
|
+
@"mention", @"code", @"u", @"i", nil];
|
|
860
|
+
|
|
861
|
+
NSMutableString *output = [NSMutableString stringWithCapacity:html.length];
|
|
862
|
+
NSMutableString *currentTagBuffer = [NSMutableString string];
|
|
863
|
+
NSCharacterSet *whitespaceAndNewlineSet =
|
|
864
|
+
[NSCharacterSet whitespaceAndNewlineCharacterSet];
|
|
865
|
+
|
|
866
|
+
BOOL isReadingTag = NO;
|
|
867
|
+
NSInteger textDepth = 0;
|
|
868
|
+
|
|
869
|
+
for (NSUInteger i = 0; i < html.length; i++) {
|
|
870
|
+
unichar c = [html characterAtIndex:i];
|
|
871
|
+
|
|
872
|
+
if (c == '<') {
|
|
873
|
+
isReadingTag = YES;
|
|
874
|
+
[currentTagBuffer setString:@""];
|
|
875
|
+
[output appendString:@"<"];
|
|
876
|
+
} else if (c == '>') {
|
|
877
|
+
isReadingTag = NO;
|
|
878
|
+
[output appendString:@">"];
|
|
879
|
+
|
|
880
|
+
NSString *fullTag = [currentTagBuffer lowercaseString];
|
|
881
|
+
|
|
882
|
+
NSString *cleanName = [fullTag
|
|
883
|
+
stringByTrimmingCharactersInSet:
|
|
884
|
+
[NSCharacterSet characterSetWithCharactersInString:@"/"]];
|
|
885
|
+
NSArray *parts =
|
|
886
|
+
[cleanName componentsSeparatedByCharactersInSet:
|
|
887
|
+
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
|
888
|
+
NSString *tagName = parts.firstObject;
|
|
889
|
+
|
|
890
|
+
if (![textTags containsObject:tagName]) {
|
|
891
|
+
continue;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
if ([fullTag hasPrefix:@"/"]) {
|
|
895
|
+
textDepth--;
|
|
896
|
+
if (textDepth < 0)
|
|
897
|
+
textDepth = 0;
|
|
898
|
+
} else {
|
|
899
|
+
// Opening tag (e.g. <h1>) -> Enter Text Mode
|
|
900
|
+
// (Ignore self-closing tags like <img/> if they happen to be in the
|
|
901
|
+
// list)
|
|
902
|
+
if (![fullTag hasSuffix:@"/"]) {
|
|
903
|
+
textDepth++;
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
} else {
|
|
907
|
+
if (isReadingTag) {
|
|
908
|
+
[currentTagBuffer appendFormat:@"%C", c];
|
|
909
|
+
[output appendFormat:@"%C", c];
|
|
910
|
+
continue;
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
if (textDepth > 0) {
|
|
914
|
+
[output appendFormat:@"%C", c];
|
|
915
|
+
} else {
|
|
916
|
+
if (![whitespaceAndNewlineSet characterIsMember:c]) {
|
|
917
|
+
[output appendFormat:@"%C", c];
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
return output;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
- (NSString *)stringByAddingNewlinesToTag:(NSString *)tag
|
|
927
|
+
inString:(NSString *)html
|
|
928
|
+
leading:(BOOL)leading
|
|
929
|
+
trailing:(BOOL)trailing {
|
|
539
930
|
NSString *str = [html copy];
|
|
540
|
-
if(leading) {
|
|
931
|
+
if (leading) {
|
|
541
932
|
NSString *formattedTag = [NSString stringWithFormat:@">%@", tag];
|
|
542
933
|
NSString *formattedNewTag = [NSString stringWithFormat:@">\n%@", tag];
|
|
543
|
-
str = [str stringByReplacingOccurrencesOfString:formattedTag
|
|
934
|
+
str = [str stringByReplacingOccurrencesOfString:formattedTag
|
|
935
|
+
withString:formattedNewTag];
|
|
544
936
|
}
|
|
545
|
-
if(trailing) {
|
|
937
|
+
if (trailing) {
|
|
546
938
|
NSString *formattedTag = [NSString stringWithFormat:@"%@<", tag];
|
|
547
939
|
NSString *formattedNewTag = [NSString stringWithFormat:@"%@\n<", tag];
|
|
548
|
-
str = [str stringByReplacingOccurrencesOfString:formattedTag
|
|
940
|
+
str = [str stringByReplacingOccurrencesOfString:formattedTag
|
|
941
|
+
withString:formattedNewTag];
|
|
549
942
|
}
|
|
550
943
|
return str;
|
|
551
944
|
}
|
|
552
945
|
|
|
553
|
-
- (void)finalizeTagEntry:(NSMutableString *)tagName
|
|
554
|
-
|
|
946
|
+
- (void)finalizeTagEntry:(NSMutableString *)tagName
|
|
947
|
+
ongoingTags:(NSMutableDictionary *)ongoingTags
|
|
948
|
+
initiallyProcessedTags:(NSMutableArray *)processedTags
|
|
949
|
+
plainText:(NSMutableString *)plainText {
|
|
555
950
|
NSMutableArray *tagEntry = [[NSMutableArray alloc] init];
|
|
556
|
-
|
|
951
|
+
|
|
557
952
|
NSArray *tagData = ongoingTags[tagName];
|
|
558
953
|
NSInteger tagLocation = [((NSNumber *)tagData[0]) intValue];
|
|
559
|
-
|
|
560
|
-
|
|
954
|
+
|
|
955
|
+
// 'tagLocation' is an index based on 'plainText' which currently only holds
|
|
956
|
+
// raw text.
|
|
957
|
+
//
|
|
958
|
+
// Since 'plainText' does not yet contain the special placeholders for images,
|
|
959
|
+
// the indices for any text following an image are lower than they will be
|
|
960
|
+
// in the final NSTextStorage.
|
|
961
|
+
//
|
|
962
|
+
// We add '_precedingImageCount' to shift the start index forward, aligning
|
|
963
|
+
// this style's range with the actual position in the final text (where each
|
|
964
|
+
// image adds 1 character).
|
|
965
|
+
NSRange tagRange = NSMakeRange(tagLocation + _precedingImageCount,
|
|
966
|
+
plainText.length - tagLocation);
|
|
967
|
+
|
|
561
968
|
[tagEntry addObject:[tagName copy]];
|
|
562
969
|
[tagEntry addObject:[NSValue valueWithRange:tagRange]];
|
|
563
|
-
if(tagData.count > 1) {
|
|
564
|
-
|
|
970
|
+
if (tagData.count > 1) {
|
|
971
|
+
[tagEntry addObject:[(NSString *)tagData[1] copy]];
|
|
565
972
|
}
|
|
566
|
-
|
|
973
|
+
|
|
567
974
|
[processedTags addObject:tagEntry];
|
|
568
975
|
[ongoingTags removeObjectForKey:tagName];
|
|
976
|
+
|
|
977
|
+
if ([tagName isEqualToString:@"img"]) {
|
|
978
|
+
_precedingImageCount++;
|
|
979
|
+
}
|
|
569
980
|
}
|
|
570
981
|
|
|
571
982
|
- (NSArray *)getTextAndStylesFromHtml:(NSString *)fixedHtml {
|
|
572
|
-
NSMutableString *plainText = [[NSMutableString alloc] initWithString
|
|
983
|
+
NSMutableString *plainText = [[NSMutableString alloc] initWithString:@""];
|
|
573
984
|
NSMutableDictionary *ongoingTags = [[NSMutableDictionary alloc] init];
|
|
574
985
|
NSMutableArray *initiallyProcessedTags = [[NSMutableArray alloc] init];
|
|
986
|
+
_precedingImageCount = 0;
|
|
575
987
|
BOOL insideTag = NO;
|
|
576
988
|
BOOL gettingTagName = NO;
|
|
577
989
|
BOOL gettingTagParams = NO;
|
|
578
990
|
BOOL closingTag = NO;
|
|
579
|
-
NSMutableString *currentTagName =
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
991
|
+
NSMutableString *currentTagName =
|
|
992
|
+
[[NSMutableString alloc] initWithString:@""];
|
|
993
|
+
NSMutableString *currentTagParams =
|
|
994
|
+
[[NSMutableString alloc] initWithString:@""];
|
|
995
|
+
NSDictionary *htmlEntitiesDict =
|
|
996
|
+
[NSString getEscapedCharactersInfoFrom:fixedHtml];
|
|
997
|
+
|
|
583
998
|
// firstly, extract text and initially processed tags
|
|
584
|
-
for(int i = 0; i < fixedHtml.length; i++) {
|
|
585
|
-
NSString *currentCharacterStr =
|
|
999
|
+
for (int i = 0; i < fixedHtml.length; i++) {
|
|
1000
|
+
NSString *currentCharacterStr =
|
|
1001
|
+
[fixedHtml substringWithRange:NSMakeRange(i, 1)];
|
|
586
1002
|
unichar currentCharacterChar = [fixedHtml characterAtIndex:i];
|
|
587
|
-
|
|
588
|
-
if(currentCharacterChar == '<') {
|
|
1003
|
+
|
|
1004
|
+
if (currentCharacterChar == '<') {
|
|
589
1005
|
// opening the tag, mark that we are inside and getting its name
|
|
590
1006
|
insideTag = YES;
|
|
591
1007
|
gettingTagName = YES;
|
|
592
|
-
} else if(currentCharacterChar == '>') {
|
|
593
|
-
// finishing some tag, no longer marked as inside or getting its
|
|
1008
|
+
} else if (currentCharacterChar == '>') {
|
|
1009
|
+
// finishing some tag, no longer marked as inside or getting its
|
|
1010
|
+
// name/params
|
|
594
1011
|
insideTag = NO;
|
|
595
1012
|
gettingTagName = NO;
|
|
596
1013
|
gettingTagParams = NO;
|
|
597
|
-
|
|
1014
|
+
|
|
598
1015
|
BOOL isSelfClosing = NO;
|
|
599
|
-
|
|
1016
|
+
|
|
600
1017
|
// Check if params ended with '/' (e.g. <img src="" />)
|
|
601
1018
|
if ([currentTagParams hasSuffix:@"/"]) {
|
|
602
|
-
[currentTagParams
|
|
1019
|
+
[currentTagParams
|
|
1020
|
+
deleteCharactersInRange:NSMakeRange(currentTagParams.length - 1,
|
|
1021
|
+
1)];
|
|
603
1022
|
isSelfClosing = YES;
|
|
604
1023
|
}
|
|
605
|
-
|
|
606
|
-
if([currentTagName isEqualToString:@"p"] ||
|
|
1024
|
+
|
|
1025
|
+
if ([currentTagName isEqualToString:@"p"] ||
|
|
1026
|
+
[currentTagName isEqualToString:@"br"] ||
|
|
1027
|
+
[currentTagName isEqualToString:@"li"]) {
|
|
607
1028
|
// do nothing, we don't include these tags in styles
|
|
608
|
-
} else if(!closingTag) {
|
|
609
|
-
// we finish opening tag - get its location and optionally params and
|
|
1029
|
+
} else if (!closingTag) {
|
|
1030
|
+
// we finish opening tag - get its location and optionally params and
|
|
1031
|
+
// put them under tag name key in ongoingTags
|
|
610
1032
|
NSMutableArray *tagArr = [[NSMutableArray alloc] init];
|
|
611
1033
|
[tagArr addObject:[NSNumber numberWithInteger:plainText.length]];
|
|
612
|
-
if(currentTagParams.length > 0) {
|
|
1034
|
+
if (currentTagParams.length > 0) {
|
|
613
1035
|
[tagArr addObject:[currentTagParams copy]];
|
|
614
1036
|
}
|
|
615
1037
|
ongoingTags[currentTagName] = tagArr;
|
|
616
|
-
|
|
617
|
-
// skip one newline after opening tags that are in separate lines
|
|
618
|
-
|
|
1038
|
+
|
|
1039
|
+
// skip one newline after opening tags that are in separate lines
|
|
1040
|
+
// intentionally
|
|
1041
|
+
if ([currentTagName isEqualToString:@"ul"] ||
|
|
1042
|
+
[currentTagName isEqualToString:@"ol"] ||
|
|
1043
|
+
[currentTagName isEqualToString:@"blockquote"] ||
|
|
1044
|
+
[currentTagName isEqualToString:@"codeblock"]) {
|
|
619
1045
|
i += 1;
|
|
620
1046
|
}
|
|
621
|
-
|
|
1047
|
+
|
|
622
1048
|
if (isSelfClosing) {
|
|
623
|
-
[self finalizeTagEntry:currentTagName
|
|
1049
|
+
[self finalizeTagEntry:currentTagName
|
|
1050
|
+
ongoingTags:ongoingTags
|
|
1051
|
+
initiallyProcessedTags:initiallyProcessedTags
|
|
1052
|
+
plainText:plainText];
|
|
624
1053
|
}
|
|
625
1054
|
} else {
|
|
626
|
-
// we finish closing tags - pack tag name, tag range and optionally tag
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
1055
|
+
// we finish closing tags - pack tag name, tag range and optionally tag
|
|
1056
|
+
// params into an entry that goes inside initiallyProcessedTags
|
|
1057
|
+
|
|
1058
|
+
// skip one newline that was added before some closing tags that are in
|
|
1059
|
+
// separate lines
|
|
1060
|
+
if ([currentTagName isEqualToString:@"ul"] ||
|
|
1061
|
+
[currentTagName isEqualToString:@"ol"] ||
|
|
1062
|
+
[currentTagName isEqualToString:@"blockquote"] ||
|
|
1063
|
+
[currentTagName isEqualToString:@"codeblock"]) {
|
|
1064
|
+
plainText = [[plainText
|
|
1065
|
+
substringWithRange:NSMakeRange(0, plainText.length - 1)]
|
|
1066
|
+
mutableCopy];
|
|
631
1067
|
}
|
|
632
|
-
|
|
633
|
-
[self finalizeTagEntry:currentTagName
|
|
1068
|
+
|
|
1069
|
+
[self finalizeTagEntry:currentTagName
|
|
1070
|
+
ongoingTags:ongoingTags
|
|
1071
|
+
initiallyProcessedTags:initiallyProcessedTags
|
|
1072
|
+
plainText:plainText];
|
|
634
1073
|
}
|
|
635
1074
|
// post-tag cleanup
|
|
636
1075
|
closingTag = NO;
|
|
637
1076
|
currentTagName = [[NSMutableString alloc] initWithString:@""];
|
|
638
1077
|
currentTagParams = [[NSMutableString alloc] initWithString:@""];
|
|
639
1078
|
} else {
|
|
640
|
-
if(!insideTag) {
|
|
1079
|
+
if (!insideTag) {
|
|
641
1080
|
// no tags logic - just append the right text
|
|
642
|
-
|
|
643
|
-
// html entity on the index; use unescaped character and forward
|
|
1081
|
+
|
|
1082
|
+
// html entity on the index; use unescaped character and forward
|
|
1083
|
+
// iterator accordingly
|
|
644
1084
|
NSArray *entityInfo = htmlEntitiesDict[@(i)];
|
|
645
|
-
if(entityInfo != nullptr) {
|
|
1085
|
+
if (entityInfo != nullptr) {
|
|
646
1086
|
NSString *escaped = entityInfo[0];
|
|
647
1087
|
NSString *unescaped = entityInfo[1];
|
|
648
1088
|
[plainText appendString:unescaped];
|
|
@@ -652,156 +1092,200 @@
|
|
|
652
1092
|
[plainText appendString:currentCharacterStr];
|
|
653
1093
|
}
|
|
654
1094
|
} else {
|
|
655
|
-
if(gettingTagName) {
|
|
656
|
-
if(currentCharacterChar == ' ') {
|
|
1095
|
+
if (gettingTagName) {
|
|
1096
|
+
if (currentCharacterChar == ' ') {
|
|
657
1097
|
// no longer getting tag name - switch to params
|
|
658
1098
|
gettingTagName = NO;
|
|
659
1099
|
gettingTagParams = YES;
|
|
660
|
-
} else if(currentCharacterChar == '/') {
|
|
1100
|
+
} else if (currentCharacterChar == '/') {
|
|
661
1101
|
// mark that the tag is closing
|
|
662
1102
|
closingTag = YES;
|
|
663
1103
|
} else {
|
|
664
1104
|
// append next tag char
|
|
665
1105
|
[currentTagName appendString:currentCharacterStr];
|
|
666
1106
|
}
|
|
667
|
-
} else if(gettingTagParams) {
|
|
1107
|
+
} else if (gettingTagParams) {
|
|
668
1108
|
// append next tag params char
|
|
669
1109
|
[currentTagParams appendString:currentCharacterStr];
|
|
670
1110
|
}
|
|
671
1111
|
}
|
|
672
1112
|
}
|
|
673
1113
|
}
|
|
674
|
-
|
|
1114
|
+
|
|
675
1115
|
// process tags into proper StyleType + StylePair values
|
|
676
1116
|
NSMutableArray *processedStyles = [[NSMutableArray alloc] init];
|
|
677
|
-
|
|
678
|
-
for(NSArray*
|
|
1117
|
+
|
|
1118
|
+
for (NSArray *arr in initiallyProcessedTags) {
|
|
679
1119
|
NSString *tagName = (NSString *)arr[0];
|
|
680
1120
|
NSValue *tagRangeValue = (NSValue *)arr[1];
|
|
681
1121
|
NSMutableString *params = [[NSMutableString alloc] initWithString:@""];
|
|
682
|
-
if(arr.count > 2) {
|
|
1122
|
+
if (arr.count > 2) {
|
|
683
1123
|
[params appendString:(NSString *)arr[2]];
|
|
684
1124
|
}
|
|
685
|
-
|
|
1125
|
+
|
|
686
1126
|
NSMutableArray *styleArr = [[NSMutableArray alloc] init];
|
|
687
1127
|
StylePair *stylePair = [[StylePair alloc] init];
|
|
688
|
-
if([tagName isEqualToString:@"b"]) {
|
|
1128
|
+
if ([tagName isEqualToString:@"b"]) {
|
|
689
1129
|
[styleArr addObject:@([BoldStyle getStyleType])];
|
|
690
|
-
} else if([tagName isEqualToString:@"i"]) {
|
|
1130
|
+
} else if ([tagName isEqualToString:@"i"]) {
|
|
691
1131
|
[styleArr addObject:@([ItalicStyle getStyleType])];
|
|
692
|
-
} else if([tagName isEqualToString:@"img"]) {
|
|
693
|
-
NSRegularExpression *srcRegex =
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
NSTextCheckingResult*
|
|
698
|
-
|
|
699
|
-
|
|
1132
|
+
} else if ([tagName isEqualToString:@"img"]) {
|
|
1133
|
+
NSRegularExpression *srcRegex =
|
|
1134
|
+
[NSRegularExpression regularExpressionWithPattern:@"src=\"([^\"]+)\""
|
|
1135
|
+
options:0
|
|
1136
|
+
error:nullptr];
|
|
1137
|
+
NSTextCheckingResult *match =
|
|
1138
|
+
[srcRegex firstMatchInString:params
|
|
1139
|
+
options:0
|
|
1140
|
+
range:NSMakeRange(0, params.length)];
|
|
1141
|
+
|
|
1142
|
+
if (match == nullptr) {
|
|
700
1143
|
continue;
|
|
701
1144
|
}
|
|
702
|
-
|
|
1145
|
+
|
|
703
1146
|
NSRange srcRange = match.range;
|
|
704
1147
|
[styleArr addObject:@([ImageStyle getStyleType])];
|
|
705
1148
|
// cut only the uri from the src="..." string
|
|
706
|
-
NSString *uri =
|
|
1149
|
+
NSString *uri =
|
|
1150
|
+
[params substringWithRange:NSMakeRange(srcRange.location + 5,
|
|
1151
|
+
srcRange.length - 6)];
|
|
707
1152
|
ImageData *imageData = [[ImageData alloc] init];
|
|
708
1153
|
imageData.uri = uri;
|
|
709
|
-
|
|
710
|
-
NSRegularExpression *widthRegex = [NSRegularExpression
|
|
711
|
-
|
|
1154
|
+
|
|
1155
|
+
NSRegularExpression *widthRegex = [NSRegularExpression
|
|
1156
|
+
regularExpressionWithPattern:@"width=\"([0-9.]+)\""
|
|
1157
|
+
options:0
|
|
1158
|
+
error:nil];
|
|
1159
|
+
NSTextCheckingResult *widthMatch =
|
|
1160
|
+
[widthRegex firstMatchInString:params
|
|
1161
|
+
options:0
|
|
1162
|
+
range:NSMakeRange(0, params.length)];
|
|
712
1163
|
|
|
713
1164
|
if (widthMatch) {
|
|
714
|
-
NSString *widthString =
|
|
1165
|
+
NSString *widthString =
|
|
1166
|
+
[params substringWithRange:[widthMatch rangeAtIndex:1]];
|
|
715
1167
|
imageData.width = [widthString floatValue];
|
|
716
1168
|
}
|
|
717
1169
|
|
|
718
|
-
NSRegularExpression *heightRegex = [NSRegularExpression
|
|
719
|
-
|
|
1170
|
+
NSRegularExpression *heightRegex = [NSRegularExpression
|
|
1171
|
+
regularExpressionWithPattern:@"height=\"([0-9.]+)\""
|
|
1172
|
+
options:0
|
|
1173
|
+
error:nil];
|
|
1174
|
+
NSTextCheckingResult *heightMatch =
|
|
1175
|
+
[heightRegex firstMatchInString:params
|
|
1176
|
+
options:0
|
|
1177
|
+
range:NSMakeRange(0, params.length)];
|
|
720
1178
|
|
|
721
1179
|
if (heightMatch) {
|
|
722
|
-
NSString *heightString =
|
|
1180
|
+
NSString *heightString =
|
|
1181
|
+
[params substringWithRange:[heightMatch rangeAtIndex:1]];
|
|
723
1182
|
imageData.height = [heightString floatValue];
|
|
724
1183
|
}
|
|
725
|
-
|
|
1184
|
+
|
|
726
1185
|
stylePair.styleValue = imageData;
|
|
727
|
-
} else if([tagName isEqualToString:@"u"]) {
|
|
1186
|
+
} else if ([tagName isEqualToString:@"u"]) {
|
|
728
1187
|
[styleArr addObject:@([UnderlineStyle getStyleType])];
|
|
729
|
-
} else if([tagName isEqualToString:@"s"]) {
|
|
1188
|
+
} else if ([tagName isEqualToString:@"s"]) {
|
|
730
1189
|
[styleArr addObject:@([StrikethroughStyle getStyleType])];
|
|
731
|
-
} else if([tagName isEqualToString:@"code"]) {
|
|
1190
|
+
} else if ([tagName isEqualToString:@"code"]) {
|
|
732
1191
|
[styleArr addObject:@([InlineCodeStyle getStyleType])];
|
|
733
|
-
} else if([tagName isEqualToString:@"a"]) {
|
|
734
|
-
NSRegularExpression *hrefRegex =
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
NSTextCheckingResult*
|
|
739
|
-
|
|
740
|
-
|
|
1192
|
+
} else if ([tagName isEqualToString:@"a"]) {
|
|
1193
|
+
NSRegularExpression *hrefRegex =
|
|
1194
|
+
[NSRegularExpression regularExpressionWithPattern:@"href=\".+\""
|
|
1195
|
+
options:0
|
|
1196
|
+
error:nullptr];
|
|
1197
|
+
NSTextCheckingResult *match =
|
|
1198
|
+
[hrefRegex firstMatchInString:params
|
|
1199
|
+
options:0
|
|
1200
|
+
range:NSMakeRange(0, params.length)];
|
|
1201
|
+
|
|
1202
|
+
if (match == nullptr) {
|
|
741
1203
|
// same as on Android, no href (or empty href) equals no link style
|
|
742
1204
|
continue;
|
|
743
1205
|
}
|
|
744
|
-
|
|
1206
|
+
|
|
745
1207
|
NSRange hrefRange = match.range;
|
|
746
1208
|
[styleArr addObject:@([LinkStyle getStyleType])];
|
|
747
1209
|
// cut only the url from the href="..." string
|
|
748
|
-
NSString *url =
|
|
1210
|
+
NSString *url =
|
|
1211
|
+
[params substringWithRange:NSMakeRange(hrefRange.location + 6,
|
|
1212
|
+
hrefRange.length - 7)];
|
|
749
1213
|
stylePair.styleValue = url;
|
|
750
|
-
} else if([tagName isEqualToString:@"mention"]) {
|
|
1214
|
+
} else if ([tagName isEqualToString:@"mention"]) {
|
|
751
1215
|
[styleArr addObject:@([MentionStyle getStyleType])];
|
|
752
1216
|
// extract html expression into dict using some regex
|
|
753
1217
|
NSMutableDictionary *paramsDict = [[NSMutableDictionary alloc] init];
|
|
754
1218
|
NSString *pattern = @"(\\w+)=\"([^\"]*)\"";
|
|
755
|
-
NSRegularExpression *regex =
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
1219
|
+
NSRegularExpression *regex =
|
|
1220
|
+
[NSRegularExpression regularExpressionWithPattern:pattern
|
|
1221
|
+
options:0
|
|
1222
|
+
error:nil];
|
|
1223
|
+
|
|
1224
|
+
[regex enumerateMatchesInString:params
|
|
1225
|
+
options:0
|
|
1226
|
+
range:NSMakeRange(0, params.length)
|
|
1227
|
+
usingBlock:^(NSTextCheckingResult *_Nullable result,
|
|
1228
|
+
NSMatchingFlags flags,
|
|
1229
|
+
BOOL *_Nonnull stop) {
|
|
1230
|
+
if (result.numberOfRanges == 3) {
|
|
1231
|
+
NSString *key = [params
|
|
1232
|
+
substringWithRange:[result rangeAtIndex:1]];
|
|
1233
|
+
NSString *value = [params
|
|
1234
|
+
substringWithRange:[result rangeAtIndex:2]];
|
|
1235
|
+
paramsDict[key] = value;
|
|
1236
|
+
}
|
|
1237
|
+
}];
|
|
1238
|
+
|
|
767
1239
|
MentionParams *mentionParams = [[MentionParams alloc] init];
|
|
768
1240
|
mentionParams.text = paramsDict[@"text"];
|
|
769
1241
|
mentionParams.indicator = paramsDict[@"indicator"];
|
|
770
|
-
|
|
771
|
-
[paramsDict removeObjectsForKeys:@[@"text", @"indicator"]];
|
|
1242
|
+
|
|
1243
|
+
[paramsDict removeObjectsForKeys:@[ @"text", @"indicator" ]];
|
|
772
1244
|
NSError *error;
|
|
773
|
-
NSData *attrsData = [NSJSONSerialization dataWithJSONObject:paramsDict
|
|
774
|
-
|
|
1245
|
+
NSData *attrsData = [NSJSONSerialization dataWithJSONObject:paramsDict
|
|
1246
|
+
options:0
|
|
1247
|
+
error:&error];
|
|
1248
|
+
NSString *formattedAttrsString =
|
|
1249
|
+
[[NSString alloc] initWithData:attrsData
|
|
1250
|
+
encoding:NSUTF8StringEncoding];
|
|
775
1251
|
mentionParams.attributes = formattedAttrsString;
|
|
776
|
-
|
|
1252
|
+
|
|
777
1253
|
stylePair.styleValue = mentionParams;
|
|
778
|
-
} else if([[tagName substringWithRange:NSMakeRange(0, 1)]
|
|
779
|
-
|
|
1254
|
+
} else if ([[tagName substringWithRange:NSMakeRange(0, 1)]
|
|
1255
|
+
isEqualToString:@"h"]) {
|
|
1256
|
+
if ([tagName isEqualToString:@"h1"]) {
|
|
780
1257
|
[styleArr addObject:@([H1Style getStyleType])];
|
|
781
|
-
} else if([tagName isEqualToString:@"h2"]) {
|
|
1258
|
+
} else if ([tagName isEqualToString:@"h2"]) {
|
|
782
1259
|
[styleArr addObject:@([H2Style getStyleType])];
|
|
783
|
-
} else if([tagName isEqualToString:@"h3"]) {
|
|
1260
|
+
} else if ([tagName isEqualToString:@"h3"]) {
|
|
784
1261
|
[styleArr addObject:@([H3Style getStyleType])];
|
|
1262
|
+
} else if ([tagName isEqualToString:@"h4"]) {
|
|
1263
|
+
[styleArr addObject:@([H4Style getStyleType])];
|
|
1264
|
+
} else if ([tagName isEqualToString:@"h5"]) {
|
|
1265
|
+
[styleArr addObject:@([H5Style getStyleType])];
|
|
1266
|
+
} else if ([tagName isEqualToString:@"h6"]) {
|
|
1267
|
+
[styleArr addObject:@([H6Style getStyleType])];
|
|
785
1268
|
}
|
|
786
|
-
} else if([tagName isEqualToString:@"ul"]) {
|
|
1269
|
+
} else if ([tagName isEqualToString:@"ul"]) {
|
|
787
1270
|
[styleArr addObject:@([UnorderedListStyle getStyleType])];
|
|
788
|
-
} else if([tagName isEqualToString:@"ol"]) {
|
|
1271
|
+
} else if ([tagName isEqualToString:@"ol"]) {
|
|
789
1272
|
[styleArr addObject:@([OrderedListStyle getStyleType])];
|
|
790
|
-
} else if([tagName isEqualToString:@"blockquote"]) {
|
|
1273
|
+
} else if ([tagName isEqualToString:@"blockquote"]) {
|
|
791
1274
|
[styleArr addObject:@([BlockQuoteStyle getStyleType])];
|
|
792
|
-
} else if([tagName isEqualToString:@"codeblock"]) {
|
|
1275
|
+
} else if ([tagName isEqualToString:@"codeblock"]) {
|
|
793
1276
|
[styleArr addObject:@([CodeBlockStyle getStyleType])];
|
|
794
1277
|
} else {
|
|
795
|
-
// some other external tags like span just don't get put into the
|
|
1278
|
+
// some other external tags like span just don't get put into the
|
|
1279
|
+
// processed styles
|
|
796
1280
|
continue;
|
|
797
1281
|
}
|
|
798
|
-
|
|
1282
|
+
|
|
799
1283
|
stylePair.rangeValue = tagRangeValue;
|
|
800
1284
|
[styleArr addObject:stylePair];
|
|
801
1285
|
[processedStyles addObject:styleArr];
|
|
802
1286
|
}
|
|
803
|
-
|
|
804
|
-
return @[plainText, processedStyles];
|
|
1287
|
+
|
|
1288
|
+
return @[ plainText, processedStyles ];
|
|
805
1289
|
}
|
|
806
1290
|
|
|
807
1291
|
@end
|