react-native-enriched 0.0.0 → 0.1.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/LICENSE +20 -0
- package/README.md +875 -0
- package/ReactNativeEnriched.podspec +27 -0
- package/android/build.gradle +101 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerDelegate.java +146 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerInterface.java +55 -0
- package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/ComponentDescriptors.cpp +22 -0
- package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/ComponentDescriptors.h +24 -0
- package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/EventEmitters.cpp +118 -0
- package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/EventEmitters.h +95 -0
- package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/Props.cpp +128 -0
- package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/Props.h +577 -0
- package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/ShadowNodes.cpp +17 -0
- package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/ShadowNodes.h +23 -0
- package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/States.cpp +16 -0
- package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/States.h +20 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/AndroidManifestNew.xml +2 -0
- package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputView.kt +535 -0
- package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewLayoutManager.kt +64 -0
- package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewManager.kt +292 -0
- package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewPackage.kt +19 -0
- package/android/src/main/java/com/swmansion/enriched/events/MentionHandler.kt +40 -0
- package/android/src/main/java/com/swmansion/enriched/events/OnChangeHtmlEvent.kt +28 -0
- package/android/src/main/java/com/swmansion/enriched/events/OnChangeSelectionEvent.kt +29 -0
- package/android/src/main/java/com/swmansion/enriched/events/OnChangeStateEvent.kt +24 -0
- package/android/src/main/java/com/swmansion/enriched/events/OnChangeTextEvent.kt +30 -0
- package/android/src/main/java/com/swmansion/enriched/events/OnInputBlurEvent.kt +27 -0
- package/android/src/main/java/com/swmansion/enriched/events/OnInputFocusEvent.kt +27 -0
- package/android/src/main/java/com/swmansion/enriched/events/OnLinkDetectedEvent.kt +30 -0
- package/android/src/main/java/com/swmansion/enriched/events/OnMentionDetectedEvent.kt +29 -0
- package/android/src/main/java/com/swmansion/enriched/events/OnMentionEvent.kt +33 -0
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedBlockQuoteSpan.kt +34 -0
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedBoldSpan.kt +10 -0
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedCodeBlockSpan.kt +38 -0
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH1Span.kt +17 -0
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH2Span.kt +17 -0
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH3Span.kt +17 -0
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedImageSpan.kt +41 -0
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedInlineCodeSpan.kt +16 -0
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedItalicSpan.kt +10 -0
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedLinkSpan.kt +24 -0
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedMentionSpan.kt +36 -0
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedOrderedListSpan.kt +71 -0
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedSpans.kt +111 -0
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedStrikeThroughSpan.kt +9 -0
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedUnderlineSpan.kt +9 -0
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedUnorderedListSpan.kt +49 -0
- package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedBlockSpan.kt +4 -0
- package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedHeadingSpan.kt +4 -0
- package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedInlineSpan.kt +4 -0
- package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedParagraphSpan.kt +4 -0
- package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedSpan.kt +4 -0
- package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedZeroWidthSpaceSpan.kt +5 -0
- package/android/src/main/java/com/swmansion/enriched/styles/HtmlStyle.kt +227 -0
- package/android/src/main/java/com/swmansion/enriched/styles/InlineStyles.kt +146 -0
- package/android/src/main/java/com/swmansion/enriched/styles/ListStyles.kt +173 -0
- package/android/src/main/java/com/swmansion/enriched/styles/ParagraphStyles.kt +186 -0
- package/android/src/main/java/com/swmansion/enriched/styles/ParametrizedStyles.kt +223 -0
- package/android/src/main/java/com/swmansion/enriched/utils/EnrichedParser.java +857 -0
- package/android/src/main/java/com/swmansion/enriched/utils/EnrichedSelection.kt +285 -0
- package/android/src/main/java/com/swmansion/enriched/utils/EnrichedSpanState.kt +204 -0
- package/android/src/main/java/com/swmansion/enriched/utils/Utils.kt +91 -0
- package/android/src/main/java/com/swmansion/enriched/watchers/EnrichedSpanWatcher.kt +73 -0
- package/android/src/main/java/com/swmansion/enriched/watchers/EnrichedTextWatcher.kt +51 -0
- package/android/src/main/new_arch/CMakeLists.txt +56 -0
- package/android/src/main/new_arch/RNEnrichedTextInputViewSpec.cpp +22 -0
- package/android/src/main/new_arch/RNEnrichedTextInputViewSpec.h +26 -0
- package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputComponentDescriptor.h +35 -0
- package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputMeasurementManager.cpp +51 -0
- package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputMeasurementManager.h +26 -0
- package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputShadowNode.cpp +34 -0
- package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputShadowNode.h +54 -0
- package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputState.cpp +9 -0
- package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputState.h +25 -0
- package/ios/EnrichedTextInputView.h +33 -0
- package/ios/EnrichedTextInputView.mm +1190 -0
- package/ios/EnrichedTextInputViewManager.mm +13 -0
- package/ios/config/InputConfig.h +67 -0
- package/ios/config/InputConfig.mm +382 -0
- package/ios/generated/RNEnrichedTextInputViewSpec/ComponentDescriptors.cpp +22 -0
- package/ios/generated/RNEnrichedTextInputViewSpec/ComponentDescriptors.h +24 -0
- package/ios/generated/RNEnrichedTextInputViewSpec/EventEmitters.cpp +118 -0
- package/ios/generated/RNEnrichedTextInputViewSpec/EventEmitters.h +95 -0
- package/ios/generated/RNEnrichedTextInputViewSpec/Props.cpp +128 -0
- package/ios/generated/RNEnrichedTextInputViewSpec/Props.h +577 -0
- package/ios/generated/RNEnrichedTextInputViewSpec/RCTComponentViewHelpers.h +384 -0
- package/ios/generated/RNEnrichedTextInputViewSpec/ShadowNodes.cpp +17 -0
- package/ios/generated/RNEnrichedTextInputViewSpec/ShadowNodes.h +23 -0
- package/ios/generated/RNEnrichedTextInputViewSpec/States.cpp +16 -0
- package/ios/generated/RNEnrichedTextInputViewSpec/States.h +20 -0
- package/ios/inputParser/InputParser.h +11 -0
- package/ios/inputParser/InputParser.mm +659 -0
- package/ios/inputTextView/InputTextView.h +6 -0
- package/ios/inputTextView/InputTextView.mm +115 -0
- package/ios/internals/EnrichedTextInputViewComponentDescriptor.h +17 -0
- package/ios/internals/EnrichedTextInputViewShadowNode.h +40 -0
- package/ios/internals/EnrichedTextInputViewShadowNode.mm +83 -0
- package/ios/internals/EnrichedTextInputViewState.cpp +10 -0
- package/ios/internals/EnrichedTextInputViewState.h +20 -0
- package/ios/styles/BlockQuoteStyle.mm +248 -0
- package/ios/styles/BoldStyle.mm +122 -0
- package/ios/styles/H1Style.mm +10 -0
- package/ios/styles/H2Style.mm +10 -0
- package/ios/styles/H3Style.mm +10 -0
- package/ios/styles/HeadingStyleBase.mm +144 -0
- package/ios/styles/InlineCodeStyle.mm +163 -0
- package/ios/styles/ItalicStyle.mm +110 -0
- package/ios/styles/LinkStyle.mm +463 -0
- package/ios/styles/MentionStyle.mm +476 -0
- package/ios/styles/OrderedListStyle.mm +225 -0
- package/ios/styles/StrikethroughStyle.mm +80 -0
- package/ios/styles/UnderlineStyle.mm +112 -0
- package/ios/styles/UnorderedListStyle.mm +225 -0
- package/ios/utils/BaseStyleProtocol.h +16 -0
- package/ios/utils/ColorExtension.h +6 -0
- package/ios/utils/ColorExtension.mm +27 -0
- package/ios/utils/FontExtension.h +13 -0
- package/ios/utils/FontExtension.mm +91 -0
- package/ios/utils/LayoutManagerExtension.h +6 -0
- package/ios/utils/LayoutManagerExtension.mm +171 -0
- package/ios/utils/LinkData.h +9 -0
- package/ios/utils/LinkData.mm +4 -0
- package/ios/utils/MentionParams.h +9 -0
- package/ios/utils/MentionParams.mm +4 -0
- package/ios/utils/MentionStyleProps.h +13 -0
- package/ios/utils/MentionStyleProps.mm +56 -0
- package/ios/utils/OccurenceUtils.h +37 -0
- package/ios/utils/OccurenceUtils.mm +124 -0
- package/ios/utils/ParagraphsUtils.h +7 -0
- package/ios/utils/ParagraphsUtils.mm +54 -0
- package/ios/utils/StringExtension.h +15 -0
- package/ios/utils/StringExtension.mm +57 -0
- package/ios/utils/StyleHeaders.h +74 -0
- package/ios/utils/StylePair.h +9 -0
- package/ios/utils/StylePair.mm +4 -0
- package/ios/utils/StyleTypeEnum.h +22 -0
- package/ios/utils/TextDecorationLineEnum.h +6 -0
- package/ios/utils/TextDecorationLineEnum.mm +4 -0
- package/ios/utils/TextInsertionUtils.h +6 -0
- package/ios/utils/TextInsertionUtils.mm +48 -0
- package/ios/utils/WordsUtils.h +6 -0
- package/ios/utils/WordsUtils.mm +88 -0
- package/ios/utils/ZeroWidthSpaceUtils.h +7 -0
- package/ios/utils/ZeroWidthSpaceUtils.mm +164 -0
- package/lib/module/EnrichedTextInput.js +191 -0
- package/lib/module/EnrichedTextInput.js.map +1 -0
- package/lib/module/EnrichedTextInputNativeComponent.ts +235 -0
- package/lib/module/index.js +4 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/normalizeHtmlStyle.js +141 -0
- package/lib/module/normalizeHtmlStyle.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/EnrichedTextInput.d.ts +113 -0
- package/lib/typescript/src/EnrichedTextInput.d.ts.map +1 -0
- package/lib/typescript/src/EnrichedTextInputNativeComponent.d.ts +160 -0
- package/lib/typescript/src/EnrichedTextInputNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +3 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/normalizeHtmlStyle.d.ts +4 -0
- package/lib/typescript/src/normalizeHtmlStyle.d.ts.map +1 -0
- package/package.json +172 -1
- package/react-native.config.js +13 -0
- package/src/EnrichedTextInput.tsx +358 -0
- package/src/EnrichedTextInputNativeComponent.ts +235 -0
- package/src/index.tsx +9 -0
- package/src/normalizeHtmlStyle.ts +188 -0
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
#import "StyleHeaders.h"
|
|
2
|
+
#import "EnrichedTextInputView.h"
|
|
3
|
+
#import "OccurenceUtils.h"
|
|
4
|
+
#import "TextInsertionUtils.h"
|
|
5
|
+
#import "UIView+React.h"
|
|
6
|
+
#import "WordsUtils.h"
|
|
7
|
+
|
|
8
|
+
// custom NSAttributedStringKeys to differentiate manually added and automatically detected links
|
|
9
|
+
static NSString *const ManualLinkAttributeName = @"ManualLinkAttributeName";
|
|
10
|
+
static NSString *const AutomaticLinkAttributeName = @"AutomaticLinkAttributeName";
|
|
11
|
+
|
|
12
|
+
@implementation LinkStyle {
|
|
13
|
+
EnrichedTextInputView *_input;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
+ (StyleType)getStyleType { return Link; }
|
|
17
|
+
|
|
18
|
+
- (instancetype)initWithInput:(id)input {
|
|
19
|
+
self = [super init];
|
|
20
|
+
_input = (EnrichedTextInputView *)input;
|
|
21
|
+
return self;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
- (void)applyStyle:(NSRange)range {
|
|
25
|
+
// no-op for links
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
- (void)addAttributes:(NSRange)range {
|
|
29
|
+
// no-op for links
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
- (void)addTypingAttributes {
|
|
33
|
+
// no-op for links
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// we have to make sure all links in the range get fully removed here
|
|
37
|
+
- (void)removeAttributes:(NSRange)range {
|
|
38
|
+
NSArray<StylePair *> *links = [self findAllOccurences:range];
|
|
39
|
+
[_input->textView.textStorage beginEditing];
|
|
40
|
+
for(StylePair *pair in links) {
|
|
41
|
+
NSRange linkRange = [self getFullLinkRangeAt:[pair.rangeValue rangeValue].location];
|
|
42
|
+
[_input->textView.textStorage removeAttribute:ManualLinkAttributeName range:linkRange];
|
|
43
|
+
[_input->textView.textStorage removeAttribute:AutomaticLinkAttributeName range:linkRange];
|
|
44
|
+
[_input->textView.textStorage addAttribute:NSForegroundColorAttributeName value:[_input->config primaryColor] range:linkRange];
|
|
45
|
+
[_input->textView.textStorage addAttribute:NSUnderlineColorAttributeName value:[_input->config primaryColor] range:linkRange];
|
|
46
|
+
[_input->textView.textStorage addAttribute:NSStrikethroughColorAttributeName value:[_input->config primaryColor] range:linkRange];
|
|
47
|
+
if([_input->config linkDecorationLine] == DecorationUnderline) {
|
|
48
|
+
[_input->textView.textStorage removeAttribute:NSUnderlineStyleAttributeName range:linkRange];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
[_input->textView.textStorage endEditing];
|
|
52
|
+
|
|
53
|
+
// adjust typing attributes as well
|
|
54
|
+
NSMutableDictionary *newTypingAttrs = [_input->textView.typingAttributes mutableCopy];
|
|
55
|
+
newTypingAttrs[NSForegroundColorAttributeName] = [_input->config primaryColor];
|
|
56
|
+
newTypingAttrs[NSUnderlineColorAttributeName] = [_input->config primaryColor];
|
|
57
|
+
newTypingAttrs[NSStrikethroughColorAttributeName] = [_input->config primaryColor];
|
|
58
|
+
if([_input->config linkDecorationLine] == DecorationUnderline) {
|
|
59
|
+
[newTypingAttrs removeObjectForKey:NSUnderlineStyleAttributeName];
|
|
60
|
+
}
|
|
61
|
+
_input->textView.typingAttributes = newTypingAttrs;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// used for conflicts, we have to remove the whole link
|
|
65
|
+
- (void)removeTypingAttributes {
|
|
66
|
+
NSRange linkRange = [self getFullLinkRangeAt:_input->textView.selectedRange.location];
|
|
67
|
+
[_input->textView.textStorage beginEditing];
|
|
68
|
+
[_input->textView.textStorage removeAttribute:ManualLinkAttributeName range:linkRange];
|
|
69
|
+
[_input->textView.textStorage removeAttribute:AutomaticLinkAttributeName range:linkRange];
|
|
70
|
+
[_input->textView.textStorage addAttribute:NSForegroundColorAttributeName value:[_input->config primaryColor] range:linkRange];
|
|
71
|
+
[_input->textView.textStorage addAttribute:NSUnderlineColorAttributeName value:[_input->config primaryColor] range:linkRange];
|
|
72
|
+
[_input->textView.textStorage addAttribute:NSStrikethroughColorAttributeName value:[_input->config primaryColor] range:linkRange];
|
|
73
|
+
if([_input->config linkDecorationLine] == DecorationUnderline) {
|
|
74
|
+
[_input->textView.textStorage removeAttribute:NSUnderlineStyleAttributeName range:linkRange];
|
|
75
|
+
}
|
|
76
|
+
[_input->textView.textStorage endEditing];
|
|
77
|
+
|
|
78
|
+
// adjust typing attributes as well
|
|
79
|
+
NSMutableDictionary *newTypingAttrs = [_input->textView.typingAttributes mutableCopy];
|
|
80
|
+
newTypingAttrs[NSForegroundColorAttributeName] = [_input->config primaryColor];
|
|
81
|
+
newTypingAttrs[NSUnderlineColorAttributeName] = [_input->config primaryColor];
|
|
82
|
+
newTypingAttrs[NSStrikethroughColorAttributeName] = [_input->config primaryColor];
|
|
83
|
+
if([_input->config linkDecorationLine] == DecorationUnderline) {
|
|
84
|
+
[newTypingAttrs removeObjectForKey:NSUnderlineStyleAttributeName];
|
|
85
|
+
}
|
|
86
|
+
_input->textView.typingAttributes = newTypingAttrs;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
- (BOOL)styleCondition:(id _Nullable)value :(NSRange)range {
|
|
90
|
+
NSString *linkValue = (NSString *)value;
|
|
91
|
+
return linkValue != nullptr;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
- (BOOL)detectStyle:(NSRange)range {
|
|
95
|
+
if(range.length >= 1) {
|
|
96
|
+
BOOL onlyLinks = [OccurenceUtils detectMultiple:@[ManualLinkAttributeName, AutomaticLinkAttributeName] withInput:_input inRange:range
|
|
97
|
+
withCondition: ^BOOL(id _Nullable value, NSRange range) {
|
|
98
|
+
return [self styleCondition:value :range];
|
|
99
|
+
}
|
|
100
|
+
];
|
|
101
|
+
return onlyLinks ? [self isSingleLinkIn:range] : NO;
|
|
102
|
+
} else {
|
|
103
|
+
return [self getLinkDataAt:range.location] != nullptr;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
- (BOOL)anyOccurence:(NSRange)range {
|
|
108
|
+
return [OccurenceUtils anyMultiple:@[ManualLinkAttributeName, AutomaticLinkAttributeName] withInput:_input inRange:range
|
|
109
|
+
withCondition:^BOOL(id _Nullable value, NSRange range) {
|
|
110
|
+
return [self styleCondition:value :range];
|
|
111
|
+
}
|
|
112
|
+
];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
- (NSArray<StylePair *> *_Nullable)findAllOccurences:(NSRange)range {
|
|
116
|
+
return [OccurenceUtils allMultiple:@[ManualLinkAttributeName, AutomaticLinkAttributeName] withInput:_input inRange:range
|
|
117
|
+
withCondition:^BOOL(id _Nullable value, NSRange range) {
|
|
118
|
+
return [self styleCondition:value :range];
|
|
119
|
+
}
|
|
120
|
+
];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// MARK: - Public non-standard methods
|
|
124
|
+
|
|
125
|
+
- (void)addLink:(NSString*)text url:(NSString*)url range:(NSRange)range manual:(BOOL)manual {
|
|
126
|
+
NSString *currentText = [_input->textView.textStorage.string substringWithRange:range];
|
|
127
|
+
|
|
128
|
+
NSMutableDictionary<NSAttributedStringKey, id> *newAttrs = [[NSMutableDictionary<NSAttributedStringKey, id> alloc] init];
|
|
129
|
+
newAttrs[NSForegroundColorAttributeName] = [_input->config linkColor];
|
|
130
|
+
newAttrs[NSUnderlineColorAttributeName] = [_input->config linkColor];
|
|
131
|
+
newAttrs[NSStrikethroughColorAttributeName] = [_input->config linkColor];
|
|
132
|
+
if([_input->config linkDecorationLine] == DecorationUnderline) {
|
|
133
|
+
newAttrs[NSUnderlineStyleAttributeName] = @(NSUnderlineStyleSingle);
|
|
134
|
+
}
|
|
135
|
+
if(manual) {
|
|
136
|
+
newAttrs[ManualLinkAttributeName] = [url copy];
|
|
137
|
+
} else {
|
|
138
|
+
newAttrs[AutomaticLinkAttributeName] = [url copy];
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if(range.length == 0) {
|
|
142
|
+
// insert link
|
|
143
|
+
[TextInsertionUtils insertText:text at:range.location additionalAttributes:newAttrs input:_input withSelection:YES];
|
|
144
|
+
} else if([currentText isEqualToString:text]) {
|
|
145
|
+
// apply link attributes
|
|
146
|
+
[_input->textView.textStorage addAttributes:newAttrs range:range];
|
|
147
|
+
// TextInsertionUtils take care of the selection but here we have to manually set it behind the link
|
|
148
|
+
// ONLY with manual links, automatic ones don't need the selection fix
|
|
149
|
+
if(manual) {
|
|
150
|
+
[_input->textView reactFocus];
|
|
151
|
+
_input->textView.selectedRange = NSMakeRange(range.location + text.length, 0);
|
|
152
|
+
}
|
|
153
|
+
} else {
|
|
154
|
+
// replace text with link
|
|
155
|
+
[TextInsertionUtils replaceText:text at:range additionalAttributes:newAttrs input:_input withSelection:YES];
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// mandatory connected links check
|
|
159
|
+
NSDictionary *currentWord = [WordsUtils getCurrentWord:_input->textView.textStorage.string range:_input->textView.selectedRange];
|
|
160
|
+
if(currentWord != nullptr) {
|
|
161
|
+
// get word properties
|
|
162
|
+
NSString *wordText = (NSString *)[currentWord objectForKey:@"word"];
|
|
163
|
+
NSValue *wordRangeValue = (NSValue *)[currentWord objectForKey:@"range"];
|
|
164
|
+
if(wordText != nullptr && wordRangeValue != nullptr) {
|
|
165
|
+
[self removeConnectedLinksIfNeeded:wordText range:[wordRangeValue rangeValue]];
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
[self manageLinkTypingAttributes];
|
|
170
|
+
[_input anyTextMayHaveBeenModified];
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// get exact link data at the given location if it exists
|
|
174
|
+
- (LinkData *)getLinkDataAt:(NSUInteger)location {
|
|
175
|
+
NSRange manualLinkRange = NSMakeRange(0, 0);
|
|
176
|
+
NSRange automaticLinkRange = NSMakeRange(0, 0);
|
|
177
|
+
NSRange inputRange = NSMakeRange(0, _input->textView.textStorage.length);
|
|
178
|
+
|
|
179
|
+
// don't search at the very end of input
|
|
180
|
+
NSUInteger searchLocation = location;
|
|
181
|
+
if(searchLocation == _input->textView.textStorage.length) {
|
|
182
|
+
return nullptr;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
NSString *manualUrl = [_input->textView.textStorage
|
|
186
|
+
attribute:ManualLinkAttributeName
|
|
187
|
+
atIndex:searchLocation
|
|
188
|
+
longestEffectiveRange: &manualLinkRange
|
|
189
|
+
inRange:inputRange
|
|
190
|
+
];
|
|
191
|
+
NSString *automaticUrl = [_input->textView.textStorage
|
|
192
|
+
attribute:AutomaticLinkAttributeName
|
|
193
|
+
atIndex:searchLocation
|
|
194
|
+
longestEffectiveRange: &automaticLinkRange
|
|
195
|
+
inRange:inputRange
|
|
196
|
+
];
|
|
197
|
+
|
|
198
|
+
if((manualUrl == nullptr && automaticUrl == nullptr) || (manualLinkRange.length == 0 && automaticLinkRange.length == 0)) {
|
|
199
|
+
return nullptr;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
NSString *linkUrl = manualUrl == nullptr ? automaticUrl : manualUrl;
|
|
203
|
+
NSRange linkRange = manualUrl == nullptr ? automaticLinkRange : manualLinkRange;
|
|
204
|
+
|
|
205
|
+
LinkData *data = [[LinkData alloc] init];
|
|
206
|
+
data.url = linkUrl;
|
|
207
|
+
data.text = [_input->textView.textStorage.string substringWithRange:linkRange];
|
|
208
|
+
return data;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// returns full range of a link at some location
|
|
212
|
+
- (NSRange)getFullLinkRangeAt:(NSUInteger)location {
|
|
213
|
+
NSRange manualLinkRange = NSMakeRange(0, 0);
|
|
214
|
+
NSRange automaticLinkRange = NSMakeRange(0, 0);
|
|
215
|
+
NSRange inputRange = NSMakeRange(0, _input->textView.textStorage.length);
|
|
216
|
+
|
|
217
|
+
// get the previous index if possible when at the very end of input
|
|
218
|
+
NSUInteger searchLocation = location;
|
|
219
|
+
if(searchLocation == _input->textView.textStorage.length) {
|
|
220
|
+
if(searchLocation == 0) {
|
|
221
|
+
return NSMakeRange(0, 0);
|
|
222
|
+
} else {
|
|
223
|
+
searchLocation = searchLocation - 1;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
[_input->textView.textStorage
|
|
228
|
+
attribute:ManualLinkAttributeName
|
|
229
|
+
atIndex:searchLocation
|
|
230
|
+
longestEffectiveRange: &manualLinkRange
|
|
231
|
+
inRange:inputRange
|
|
232
|
+
];
|
|
233
|
+
[_input->textView.textStorage
|
|
234
|
+
attribute:AutomaticLinkAttributeName
|
|
235
|
+
atIndex:searchLocation
|
|
236
|
+
longestEffectiveRange: &automaticLinkRange
|
|
237
|
+
inRange:inputRange
|
|
238
|
+
];
|
|
239
|
+
|
|
240
|
+
return manualLinkRange.length == 0 ? automaticLinkRange : manualLinkRange;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
- (void)manageLinkTypingAttributes {
|
|
244
|
+
// link's typing attribtues need to be removed at ALL times whenever we have some link around
|
|
245
|
+
BOOL removeAttrs = NO;
|
|
246
|
+
|
|
247
|
+
if(_input->textView.selectedRange.length == 0) {
|
|
248
|
+
// check before
|
|
249
|
+
if(_input->textView.selectedRange.location >= 1) {
|
|
250
|
+
if([self detectStyle:NSMakeRange(_input->textView.selectedRange.location - 1, 1)]) {
|
|
251
|
+
removeAttrs = YES;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// check after
|
|
255
|
+
if(_input->textView.selectedRange.location < _input->textView.textStorage.length) {
|
|
256
|
+
if([self detectStyle:NSMakeRange(_input->textView.selectedRange.location, 1)]) {
|
|
257
|
+
removeAttrs = YES;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
} else {
|
|
261
|
+
if([self anyOccurence:_input->textView.selectedRange]) {
|
|
262
|
+
removeAttrs = YES;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if(removeAttrs) {
|
|
267
|
+
NSMutableDictionary *newTypingAttrs = [_input->textView.typingAttributes mutableCopy];
|
|
268
|
+
newTypingAttrs[NSForegroundColorAttributeName] = [_input->config primaryColor];
|
|
269
|
+
newTypingAttrs[NSUnderlineColorAttributeName] = [_input->config primaryColor];
|
|
270
|
+
newTypingAttrs[NSStrikethroughColorAttributeName] = [_input->config primaryColor];
|
|
271
|
+
if([_input->config linkDecorationLine] == DecorationUnderline) {
|
|
272
|
+
[newTypingAttrs removeObjectForKey:NSUnderlineStyleAttributeName];
|
|
273
|
+
}
|
|
274
|
+
_input->textView.typingAttributes = newTypingAttrs;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// handles detecting and removing automatic links
|
|
279
|
+
- (void)handleAutomaticLinks:(NSString *)word inRange:(NSRange)wordRange {
|
|
280
|
+
InlineCodeStyle *inlineCodeStyle = [_input->stylesDict objectForKey:@([InlineCodeStyle getStyleType])];
|
|
281
|
+
MentionStyle *mentionStyle = [_input->stylesDict objectForKey:@([MentionStyle getStyleType])];
|
|
282
|
+
|
|
283
|
+
if (inlineCodeStyle == nullptr || mentionStyle == nullptr) {
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// we don't recognize links along mentions
|
|
288
|
+
if ([mentionStyle anyOccurence:wordRange]) {
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// we don't recognize links among inline code
|
|
293
|
+
if ([inlineCodeStyle anyOccurence:wordRange]) {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// remove connected different links
|
|
298
|
+
[self removeConnectedLinksIfNeeded:word range:wordRange];
|
|
299
|
+
|
|
300
|
+
// we don't recognize automatic links along manual ones
|
|
301
|
+
__block BOOL manualLinkPresent = NO;
|
|
302
|
+
[_input->textView.textStorage enumerateAttribute:ManualLinkAttributeName inRange:wordRange options:0
|
|
303
|
+
usingBlock:^(id value, NSRange range, BOOL *stop) {
|
|
304
|
+
NSString *urlValue = (NSString *)value;
|
|
305
|
+
if(urlValue != nullptr) {
|
|
306
|
+
manualLinkPresent = YES;
|
|
307
|
+
*stop = YES;
|
|
308
|
+
}
|
|
309
|
+
}];
|
|
310
|
+
if(manualLinkPresent) {
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
NSRegularExpression *fullRegex = [NSRegularExpression regularExpressionWithPattern:@"http(s)?:\\/\\/www\\.[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)"
|
|
315
|
+
options:0
|
|
316
|
+
error:nullptr
|
|
317
|
+
];
|
|
318
|
+
NSRegularExpression *wwwRegex = [NSRegularExpression regularExpressionWithPattern:@"www\\.[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)"
|
|
319
|
+
options:0
|
|
320
|
+
error:nullptr
|
|
321
|
+
];
|
|
322
|
+
NSRegularExpression *bareRegex = [NSRegularExpression regularExpressionWithPattern:@"[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)"
|
|
323
|
+
options:0
|
|
324
|
+
error:nullptr
|
|
325
|
+
];
|
|
326
|
+
|
|
327
|
+
NSString *regexPassedUrl = nullptr;
|
|
328
|
+
|
|
329
|
+
if ([fullRegex numberOfMatchesInString:word options:0 range:NSMakeRange(0, word.length)]) {
|
|
330
|
+
regexPassedUrl = word;
|
|
331
|
+
} else if ([wwwRegex numberOfMatchesInString:word options:0 range:NSMakeRange(0, word.length)]) {
|
|
332
|
+
regexPassedUrl = word;
|
|
333
|
+
} else if ([bareRegex numberOfMatchesInString:word options:0 range:NSMakeRange(0, word.length)]) {
|
|
334
|
+
regexPassedUrl = word;
|
|
335
|
+
} else if ([self anyOccurence:wordRange]) {
|
|
336
|
+
// there was some automatic link (because anyOccurence is true and we are sure there are no manual links)
|
|
337
|
+
// still, it didn't pass any regex - needs to be removed
|
|
338
|
+
[self removeAttributes:wordRange];
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if(regexPassedUrl != nullptr) {
|
|
342
|
+
// add style only if needed
|
|
343
|
+
BOOL addStyle = YES;
|
|
344
|
+
if([self detectStyle:wordRange]) {
|
|
345
|
+
LinkData *currentData = [self getLinkDataAt:wordRange.location];
|
|
346
|
+
if(currentData != nullptr && currentData.url != nullptr && [currentData.url isEqualToString:regexPassedUrl]) {
|
|
347
|
+
addStyle = NO;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
if(addStyle) {
|
|
351
|
+
[self addLink:word url:regexPassedUrl range:wordRange manual:NO];
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// emit onLinkDetected
|
|
355
|
+
[_input emitOnLinkDetectedEvent:word url:regexPassedUrl range:wordRange];
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// handles refreshing manual links
|
|
360
|
+
- (void)handleManualLinks:(NSString *)word inRange:(NSRange)wordRange {
|
|
361
|
+
// look for manual links within the word
|
|
362
|
+
__block NSString *manualLinkMinValue = @"";
|
|
363
|
+
__block NSString *manualLinkMaxValue = @"";
|
|
364
|
+
__block NSInteger manualLinkMinIdx = -1;
|
|
365
|
+
__block NSInteger manualLinkMaxIdx = -1;
|
|
366
|
+
|
|
367
|
+
[_input->textView.textStorage enumerateAttribute:ManualLinkAttributeName inRange:wordRange options:0
|
|
368
|
+
usingBlock:^(id value, NSRange range, BOOL *stop) {
|
|
369
|
+
NSString *urlValue = (NSString *)value;
|
|
370
|
+
if (urlValue != nullptr) {
|
|
371
|
+
NSInteger linkMin = range.location;
|
|
372
|
+
NSInteger linkMax = range.location + range.length - 1;
|
|
373
|
+
if (manualLinkMinIdx == -1 || linkMin < manualLinkMinIdx) {
|
|
374
|
+
manualLinkMinIdx = linkMin;
|
|
375
|
+
manualLinkMinValue = value;
|
|
376
|
+
}
|
|
377
|
+
if (manualLinkMaxIdx == -1 || linkMax > manualLinkMaxIdx) {
|
|
378
|
+
manualLinkMaxIdx = linkMax;
|
|
379
|
+
manualLinkMaxValue = value;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}];
|
|
383
|
+
|
|
384
|
+
// no manual links
|
|
385
|
+
if(manualLinkMinIdx == -1 || manualLinkMaxIdx == -1) {
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// heuristic for refreshing manual links:
|
|
390
|
+
// we update the Manual attribute between the bounds of existing ones
|
|
391
|
+
// we do that only if the bounds point to the same url
|
|
392
|
+
// this way manual link gets "extended" only if some characters were added inside it
|
|
393
|
+
if([manualLinkMinValue isEqualToString:manualLinkMaxValue]) {
|
|
394
|
+
NSRange newRange = NSMakeRange(manualLinkMinIdx, manualLinkMaxIdx - manualLinkMinIdx + 1);
|
|
395
|
+
[_input->textView.textStorage addAttribute:NSForegroundColorAttributeName value:[_input->config linkColor] range:newRange];
|
|
396
|
+
[_input->textView.textStorage addAttribute:NSUnderlineColorAttributeName value:[_input->config linkColor] range:newRange];
|
|
397
|
+
[_input->textView.textStorage addAttribute:NSStrikethroughColorAttributeName value:[_input->config linkColor] range:newRange];
|
|
398
|
+
if([_input->config linkDecorationLine] == DecorationUnderline) {
|
|
399
|
+
[_input->textView.textStorage addAttribute:NSUnderlineStyleAttributeName value:@(NSUnderlineStyleSingle) range:newRange];
|
|
400
|
+
}
|
|
401
|
+
[_input->textView.textStorage addAttribute:ManualLinkAttributeName value:manualLinkMinValue range:newRange];
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// link typing attributes need to be fixed after these changes
|
|
405
|
+
[self manageLinkTypingAttributes];
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// replacing whole input (that starts with a link) with a manually typed letter improperly applies link's attributes to all the following text
|
|
409
|
+
- (BOOL)handleLeadingLinkReplacement:(NSRange)range replacementText:(NSString *)text {
|
|
410
|
+
// whole textView range gets replaced with a single letter
|
|
411
|
+
if(_input->textView.textStorage.string.length > 0 && NSEqualRanges(range, NSMakeRange(0, _input->textView.textStorage.string.length)) && text.length == 1) {
|
|
412
|
+
// first character detection is enough for the removal to be done
|
|
413
|
+
if([self detectStyle:NSMakeRange(0, 1)]) {
|
|
414
|
+
[self removeAttributes:NSMakeRange(0, _input->textView.textStorage.string.length)];
|
|
415
|
+
// do the replacing manually
|
|
416
|
+
[TextInsertionUtils replaceText:text at:range additionalAttributes:nullptr input:_input withSelection:YES];
|
|
417
|
+
return YES;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
return NO;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// MARK: - Private non-standard methods
|
|
424
|
+
|
|
425
|
+
// determines whether a given range contains only links pointing to one url
|
|
426
|
+
// assumes the whole range is links only already
|
|
427
|
+
- (BOOL)isSingleLinkIn:(NSRange)range {
|
|
428
|
+
return [self findAllOccurences:range].count == 1;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
- (void)removeConnectedLinksIfNeeded:(NSString *)word range:(NSRange)wordRange {
|
|
432
|
+
BOOL anyAutomatic = [OccurenceUtils any:AutomaticLinkAttributeName withInput:_input inRange:wordRange
|
|
433
|
+
withCondition: ^BOOL(id _Nullable value, NSRange range) {
|
|
434
|
+
return [self styleCondition:value :range];
|
|
435
|
+
}
|
|
436
|
+
];
|
|
437
|
+
BOOL anyManual = [OccurenceUtils any:ManualLinkAttributeName withInput:_input inRange:wordRange
|
|
438
|
+
withCondition: ^BOOL(id _Nullable value, NSRange range) {
|
|
439
|
+
return [self styleCondition:value :range];
|
|
440
|
+
}
|
|
441
|
+
];
|
|
442
|
+
|
|
443
|
+
// both manual and automatic links are somewhere - delete!
|
|
444
|
+
if(anyAutomatic && anyManual) {
|
|
445
|
+
[self removeAttributes:wordRange];
|
|
446
|
+
[self manageLinkTypingAttributes];
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// we are now sure there is only one type of link there - and make sure it covers the whole word
|
|
450
|
+
BOOL onlyLinks = [OccurenceUtils detectMultiple:@[ManualLinkAttributeName, AutomaticLinkAttributeName] withInput:_input inRange:wordRange
|
|
451
|
+
withCondition: ^BOOL(id _Nullable value, NSRange range) {
|
|
452
|
+
return [self styleCondition:value :range];
|
|
453
|
+
}
|
|
454
|
+
];
|
|
455
|
+
|
|
456
|
+
// only one link might be present!
|
|
457
|
+
if(onlyLinks && ![self isSingleLinkIn:wordRange]) {
|
|
458
|
+
[self removeAttributes:wordRange];
|
|
459
|
+
[self manageLinkTypingAttributes];
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
@end
|