+GF Offline Translator:
+text and speech translation for 14 languages with
+quality control
+
+
+Speech input : Tap microphone icon if available on the keyboard.
+
+Text input : Select the text box at the bottom on the screen.
+
+Correction : Tap input text to copy it to the text box.
+
+Alternatives : Tap output text.
+
+Grammar info : Tap any of the alternatives.
+
+Confidence : colour of output text
+
+
Green : semantic, probably correct (but has alternatives)
+
Yellow : syntactic, often incorrect (has alternatives)
+
Light red : chunk-based, probably incorrect (has alternatives)
+
Dark red : word-based, often very bad
+
+
+
+
+
+
More details
+
+GF Offline Translator is based on grammar and semantics. It is compact in size
+and gives control on quality. Its technology is inspired by compilers, which are
+programs that translate computer languages.
+Most other translators for human language are based on
+statistics and have less control of quality and are much bigger, so that
+they require either an internet connection or a huge storage on your phone.
+
+The app indicates translation confidence with colours:
+
+
Green:
+ semantic translation, should be correct.
+ But not necessarily the only correct one.
+ You can tap the output to see alternatives.
+
+
Yellow:
+ syntactic translation, should be grammatically correct.
+ But can be very strange in its interpretation and choice of words.
+
+
Light red:
+ chunk translation, probably incorrect.
+ Builds the translation from small pieces.
+
+
Dark red:
+ word-by-word translation, almost certainly incorrect.
+ Builds the translation word by word.
+
+
+The green translations come from a tourist phrasebook, which allows
+you to translate things like "hello" and "how far is the airport from
+the hotel".
+
+
+
+Translation works between any supported languages, which means 182
+language pairs in the current version. But different languages are on different levels of development.
+The following table gives a rough idea of what to expect:
+
+
+
+
+
+
coverage
quality
speed
+
Bulgarian
+
Catalan
+
Chinese
+
Dutch
+
English
+
Finnish
+
French
+
German
+
Hindi
+
Italian
+
Japanese
+
Spanish
+
Swedish
+
Thai
+
+
+
+For Japanse and Thai input, words must be separated with spaces.
+
+
+
+When you tap on a translation you get a screen with alternative translations.
+Tapping on each of the alternatives
+gives you grammatical information:
+an inflection table, if it is a single word,
+and a syntax tree otherwise.
+
+
+
+To make it easier to input what you want to translate it is recommended to add that language keyboard in your settings on your iOS device.
+You can add keyboards from Settings > General > Keyboard > Keyboards.
+
+If more than one language is added, you can change the language while typing. Follow these steps to change the language of your keyboard once you’ve added it:
+
Tap the globe icon in the lower left corner to choose the next language on your keyboard. The chosen language appears on the Space bar.
+
Touch and hold the globe icon in the lower left to display a menu of enabled keyboards. Drag to choose a new keyboard.
+
+
+
+The translation works completely off-line, without
+internet connection, when doing text-based translation.
+Speech input needs an connection to work.
+
+
+
+The GF Offline Translator is powered by
+GF, Grammatical Framework.
+It is open-source software,
+built by support from the GF community and from Digital Grammars.
+
+
+
+Digital Grammars is a company that can tailor this app to you needs and provide good
+translation for the kind of vocabulary you need. Just tell us what you want to see
+ in the green area!
+
+
+
+
diff --git a/src/ui/ios/GF-Offline-Translator/GF Offline Translator/main.m b/src/ui/ios/GF-Offline-Translator/GF Offline Translator/main.m
new file mode 100644
index 000000000..c1018b339
--- /dev/null
+++ b/src/ui/ios/GF-Offline-Translator/GF Offline Translator/main.m
@@ -0,0 +1,16 @@
+//
+// main.m
+// GF Offline Translator
+//
+// Created by Cenny Davidsson on 2015-04-27.
+// Copyright (c) 2015 Grammatical Framework. All rights reserved.
+//
+
+#import
+#import "AppDelegate.h"
+
+int main(int argc, char * argv[]) {
+ @autoreleasepool {
+ return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
+ }
+}
diff --git a/src/ui/ios/GF-Offline-Translator/GF Offline TranslatorTests/GF_Offline_TranslatorTests.m b/src/ui/ios/GF-Offline-Translator/GF Offline TranslatorTests/GF_Offline_TranslatorTests.m
new file mode 100644
index 000000000..1e514080d
--- /dev/null
+++ b/src/ui/ios/GF-Offline-Translator/GF Offline TranslatorTests/GF_Offline_TranslatorTests.m
@@ -0,0 +1,40 @@
+//
+// GF_Offline_TranslatorTests.m
+// GF Offline TranslatorTests
+//
+// Created by Cenny Davidsson on 2015-04-27.
+// Copyright (c) 2015 Grammatical Framework. All rights reserved.
+//
+
+#import
+#import
+
+@interface GF_Offline_TranslatorTests : XCTestCase
+
+@end
+
+@implementation GF_Offline_TranslatorTests
+
+- (void)setUp {
+ [super setUp];
+ // Put setup code here. This method is called before the invocation of each test method in the class.
+}
+
+- (void)tearDown {
+ // Put teardown code here. This method is called after the invocation of each test method in the class.
+ [super tearDown];
+}
+
+- (void)testExample {
+ // This is an example of a functional test case.
+ XCTAssert(YES, @"Pass");
+}
+
+- (void)testPerformanceExample {
+ // This is an example of a performance test case.
+ [self measureBlock:^{
+ // Put the code you want to measure the time of here.
+ }];
+}
+
+@end
diff --git a/src/ui/ios/GF-Offline-Translator/GF Offline TranslatorTests/Info.plist b/src/ui/ios/GF-Offline-Translator/GF Offline TranslatorTests/Info.plist
new file mode 100644
index 000000000..fe7f434fb
--- /dev/null
+++ b/src/ui/ios/GF-Offline-Translator/GF Offline TranslatorTests/Info.plist
@@ -0,0 +1,24 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ GF.$(PRODUCT_NAME:rfc1034identifier)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ BNDL
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1
+
+
diff --git a/src/ui/ios/GF-Offline-Translator/Podfile b/src/ui/ios/GF-Offline-Translator/Podfile
new file mode 100644
index 000000000..342870005
--- /dev/null
+++ b/src/ui/ios/GF-Offline-Translator/Podfile
@@ -0,0 +1,5 @@
+# Uncomment this line to define a global platform for your project
+# platform :ios, ’8.0’
+
+pod 'SlackTextViewController'
+
diff --git a/src/ui/ios/GF-Offline-Translator/Podfile.lock b/src/ui/ios/GF-Offline-Translator/Podfile.lock
new file mode 100644
index 000000000..e8e8f7caa
--- /dev/null
+++ b/src/ui/ios/GF-Offline-Translator/Podfile.lock
@@ -0,0 +1,10 @@
+PODS:
+ - SlackTextViewController (1.5.2)
+
+DEPENDENCIES:
+ - SlackTextViewController
+
+SPEC CHECKSUMS:
+ SlackTextViewController: ab240cda4c7fe1f70f6c6cf64578f3fea495ce99
+
+COCOAPODS: 0.36.1
diff --git a/src/ui/ios/GF-Offline-Translator/Pods/Manifest.lock b/src/ui/ios/GF-Offline-Translator/Pods/Manifest.lock
new file mode 100644
index 000000000..e8e8f7caa
--- /dev/null
+++ b/src/ui/ios/GF-Offline-Translator/Pods/Manifest.lock
@@ -0,0 +1,10 @@
+PODS:
+ - SlackTextViewController (1.5.2)
+
+DEPENDENCIES:
+ - SlackTextViewController
+
+SPEC CHECKSUMS:
+ SlackTextViewController: ab240cda4c7fe1f70f6c6cf64578f3fea495ce99
+
+COCOAPODS: 0.36.1
diff --git a/src/ui/ios/GF-Offline-Translator/Pods/Pods.xcodeproj/project.pbxproj b/src/ui/ios/GF-Offline-Translator/Pods/Pods.xcodeproj/project.pbxproj
new file mode 100644
index 000000000..feb81b1e7
--- /dev/null
+++ b/src/ui/ios/GF-Offline-Translator/Pods/Pods.xcodeproj/project.pbxproj
@@ -0,0 +1,499 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 07DA840C480DEF98FC6F0E0D /* UIScrollView+SLKAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = F294343CD38A6789868EF3AB /* UIScrollView+SLKAdditions.m */; };
+ 16C7AE8DEA0077FB3D896BF0 /* SLKTextView.h in Headers */ = {isa = PBXBuildFile; fileRef = 7055B434E17A758334C4D690 /* SLKTextView.h */; };
+ 32678D01710471A868EEF0AE /* SLKUIConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = 784A38EF52EB47CE5E03AFEA /* SLKUIConstants.h */; };
+ 32A92A492370904FDA401B29 /* UIView+SLKAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 201D11D9F140BA666D2ADC73 /* UIView+SLKAdditions.h */; };
+ 42927E531994B740240AD1F8 /* SLKTextViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F0A7CDEDA02B55960040DCE /* SLKTextViewController.h */; };
+ 457D66B4156AA1908749B089 /* SLKInputAccessoryView.m in Sources */ = {isa = PBXBuildFile; fileRef = 60EFC05743E457629F6FA3BE /* SLKInputAccessoryView.m */; };
+ 5D47E178549F6D3808494181 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B41EF424A850AF9597402D7B /* Foundation.framework */; };
+ 5EC3EF97656A41D44CF363B4 /* SLKTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = FAB2A620AECF49DF8EC027EB /* SLKTextView.m */; };
+ 63EEAE6BC1F6324007967880 /* UIView+SLKAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = E91CE9439FF285A3B1918133 /* UIView+SLKAdditions.m */; };
+ 8276AAEEBBAF4FEE31EEF70A /* SLKTypingIndicatorView.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F3B904D76A0932EDC0DDC6C /* SLKTypingIndicatorView.h */; };
+ 8F8598074DC5378DE846D9F7 /* SLKTextInputbar.h in Headers */ = {isa = PBXBuildFile; fileRef = CDD2299FFE62AC33DD5F6F21 /* SLKTextInputbar.h */; };
+ A20C8CD4FA62A08EA32FB320 /* Pods-SlackTextViewController-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = A33FBE323AB331AA8305AE77 /* Pods-SlackTextViewController-dummy.m */; };
+ A7A374D1CD4744F4774FAD99 /* SLKTextView+SLKAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 2630AA286F47284320CEDD6D /* SLKTextView+SLKAdditions.h */; };
+ A891E95D7B87B4E183738431 /* SLKTypingIndicatorView.m in Sources */ = {isa = PBXBuildFile; fileRef = 172242421980D777AB0996D9 /* SLKTypingIndicatorView.m */; };
+ B294EBBAAA27FB04D4FE8DE0 /* Pods-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = E8468D560351CE3CFD012400 /* Pods-dummy.m */; };
+ BF4B84A5AA10198C93EF73AB /* SLKTextView+SLKAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E88530B4D45296016366C45 /* SLKTextView+SLKAdditions.m */; };
+ D484455BB70A68645DA6474A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B41EF424A850AF9597402D7B /* Foundation.framework */; };
+ DAB3B22291DFA7F6D080EE66 /* UIResponder+SLKAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 3ADF674E34301784035DA916 /* UIResponder+SLKAdditions.m */; };
+ E8035C87DFD1E736BE9CCD9A /* SLKInputAccessoryView.h in Headers */ = {isa = PBXBuildFile; fileRef = EAF2276CEA5924314C150535 /* SLKInputAccessoryView.h */; };
+ F1221A6E5E840FB484D12BFD /* UIResponder+SLKAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 9827A079F152A80A7F332466 /* UIResponder+SLKAdditions.h */; };
+ F3F121D9C25CBF2C8C5CB6FD /* SLKTextInputbar.m in Sources */ = {isa = PBXBuildFile; fileRef = 9FF5C109F0F229E7A27AE4D5 /* SLKTextInputbar.m */; };
+ FA0F22AE0A83CE07B10A234A /* SLKTextViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1930DF3ED38A6053FBBC8322 /* SLKTextViewController.m */; };
+ FB64B1B87CE3B6BBF828598E /* UIScrollView+SLKAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = B66AACC28C7F958977E64248 /* UIScrollView+SLKAdditions.h */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 542E4E1AA94F676B09AEE37B /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 8B26729A3C6DF0B6B83B55BB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = BB9541CC3748C1191DEF8D25;
+ remoteInfo = "Pods-SlackTextViewController";
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXFileReference section */
+ 0E88530B4D45296016366C45 /* SLKTextView+SLKAdditions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "SLKTextView+SLKAdditions.m"; path = "Source/Additions/SLKTextView+SLKAdditions.m"; sourceTree = ""; };
+ 15722C6B56D29EE5F9D7B918 /* Pods-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-acknowledgements.markdown"; sourceTree = ""; };
+ 172242421980D777AB0996D9 /* SLKTypingIndicatorView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SLKTypingIndicatorView.m; path = Source/Classes/SLKTypingIndicatorView.m; sourceTree = ""; };
+ 1930DF3ED38A6053FBBC8322 /* SLKTextViewController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SLKTextViewController.m; path = Source/Classes/SLKTextViewController.m; sourceTree = ""; };
+ 1B88B007510869623C9B933B /* Pods-SlackTextViewController.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-SlackTextViewController.xcconfig"; sourceTree = ""; };
+ 1F0A7CDEDA02B55960040DCE /* SLKTextViewController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SLKTextViewController.h; path = Source/Classes/SLKTextViewController.h; sourceTree = ""; };
+ 1F3B904D76A0932EDC0DDC6C /* SLKTypingIndicatorView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SLKTypingIndicatorView.h; path = Source/Classes/SLKTypingIndicatorView.h; sourceTree = ""; };
+ 201D11D9F140BA666D2ADC73 /* UIView+SLKAdditions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIView+SLKAdditions.h"; path = "Source/Additions/UIView+SLKAdditions.h"; sourceTree = ""; };
+ 2630AA286F47284320CEDD6D /* SLKTextView+SLKAdditions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "SLKTextView+SLKAdditions.h"; path = "Source/Additions/SLKTextView+SLKAdditions.h"; sourceTree = ""; };
+ 2ECB69507C18B14A1DD3CE5E /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Pods.debug.xcconfig; sourceTree = ""; };
+ 3665C875F733140A55AD452B /* Podfile */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
+ 383F6CE99248AAF4C3B60881 /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Pods.release.xcconfig; sourceTree = ""; };
+ 3ADF674E34301784035DA916 /* UIResponder+SLKAdditions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIResponder+SLKAdditions.m"; path = "Source/Additions/UIResponder+SLKAdditions.m"; sourceTree = ""; };
+ 3D3783BDE4D26D1475521002 /* Pods-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-resources.sh"; sourceTree = ""; };
+ 4449CD8C2533D423CEE6B594 /* libPods-SlackTextViewController.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-SlackTextViewController.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 60EFC05743E457629F6FA3BE /* SLKInputAccessoryView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SLKInputAccessoryView.m; path = Source/Classes/SLKInputAccessoryView.m; sourceTree = ""; };
+ 6591BD5CAF7C70A208E5C5DB /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ 7055B434E17A758334C4D690 /* SLKTextView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SLKTextView.h; path = Source/Classes/SLKTextView.h; sourceTree = ""; };
+ 784A38EF52EB47CE5E03AFEA /* SLKUIConstants.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SLKUIConstants.h; path = Source/Additions/SLKUIConstants.h; sourceTree = ""; };
+ 9827A079F152A80A7F332466 /* UIResponder+SLKAdditions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIResponder+SLKAdditions.h"; path = "Source/Additions/UIResponder+SLKAdditions.h"; sourceTree = ""; };
+ 9C15325AC04364EFC436831E /* Pods-SlackTextViewController-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-SlackTextViewController-prefix.pch"; sourceTree = ""; };
+ 9FF5C109F0F229E7A27AE4D5 /* SLKTextInputbar.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SLKTextInputbar.m; path = Source/Classes/SLKTextInputbar.m; sourceTree = ""; };
+ A33FBE323AB331AA8305AE77 /* Pods-SlackTextViewController-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-SlackTextViewController-dummy.m"; sourceTree = ""; };
+ A5535153937D8E4BC12C7188 /* Pods-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-acknowledgements.plist"; sourceTree = ""; };
+ B1C30DBE70E62F5C3FEF4944 /* Pods-SlackTextViewController-Private.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-SlackTextViewController-Private.xcconfig"; sourceTree = ""; };
+ B41EF424A850AF9597402D7B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS7.1.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };
+ B66AACC28C7F958977E64248 /* UIScrollView+SLKAdditions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIScrollView+SLKAdditions.h"; path = "Source/Additions/UIScrollView+SLKAdditions.h"; sourceTree = ""; };
+ CDD2299FFE62AC33DD5F6F21 /* SLKTextInputbar.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SLKTextInputbar.h; path = Source/Classes/SLKTextInputbar.h; sourceTree = ""; };
+ E8468D560351CE3CFD012400 /* Pods-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-dummy.m"; sourceTree = ""; };
+ E91CE9439FF285A3B1918133 /* UIView+SLKAdditions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIView+SLKAdditions.m"; path = "Source/Additions/UIView+SLKAdditions.m"; sourceTree = ""; };
+ EAF2276CEA5924314C150535 /* SLKInputAccessoryView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SLKInputAccessoryView.h; path = Source/Classes/SLKInputAccessoryView.h; sourceTree = ""; };
+ F294343CD38A6789868EF3AB /* UIScrollView+SLKAdditions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIScrollView+SLKAdditions.m"; path = "Source/Additions/UIScrollView+SLKAdditions.m"; sourceTree = ""; };
+ F66D65D2DDA9DCEB56C74350 /* Pods-environment.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-environment.h"; sourceTree = ""; };
+ FAB2A620AECF49DF8EC027EB /* SLKTextView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SLKTextView.m; path = Source/Classes/SLKTextView.m; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ C4247E842C0701BB7FF50710 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ D484455BB70A68645DA6474A /* Foundation.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FBE955E2F286A978C3471329 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 5D47E178549F6D3808494181 /* Foundation.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 04FBB9A3894582C5C79339A6 /* Support Files */ = {
+ isa = PBXGroup;
+ children = (
+ 1B88B007510869623C9B933B /* Pods-SlackTextViewController.xcconfig */,
+ B1C30DBE70E62F5C3FEF4944 /* Pods-SlackTextViewController-Private.xcconfig */,
+ A33FBE323AB331AA8305AE77 /* Pods-SlackTextViewController-dummy.m */,
+ 9C15325AC04364EFC436831E /* Pods-SlackTextViewController-prefix.pch */,
+ );
+ name = "Support Files";
+ path = "../Target Support Files/Pods-SlackTextViewController";
+ sourceTree = "";
+ };
+ 413C40B15216709EE64D28AF /* Pods */ = {
+ isa = PBXGroup;
+ children = (
+ 15722C6B56D29EE5F9D7B918 /* Pods-acknowledgements.markdown */,
+ A5535153937D8E4BC12C7188 /* Pods-acknowledgements.plist */,
+ E8468D560351CE3CFD012400 /* Pods-dummy.m */,
+ F66D65D2DDA9DCEB56C74350 /* Pods-environment.h */,
+ 3D3783BDE4D26D1475521002 /* Pods-resources.sh */,
+ 2ECB69507C18B14A1DD3CE5E /* Pods.debug.xcconfig */,
+ 383F6CE99248AAF4C3B60881 /* Pods.release.xcconfig */,
+ );
+ name = Pods;
+ path = "Target Support Files/Pods";
+ sourceTree = "";
+ };
+ 427BECA3398D88D929354AB6 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 6591BD5CAF7C70A208E5C5DB /* libPods.a */,
+ 4449CD8C2533D423CEE6B594 /* libPods-SlackTextViewController.a */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 7DE09A3B7A1C6B5C8C44A7EF /* Targets Support Files */ = {
+ isa = PBXGroup;
+ children = (
+ 413C40B15216709EE64D28AF /* Pods */,
+ );
+ name = "Targets Support Files";
+ sourceTree = "";
+ };
+ 9CFAE8514509E6C3EF2B39B3 /* SlackTextViewController */ = {
+ isa = PBXGroup;
+ children = (
+ EAF2276CEA5924314C150535 /* SLKInputAccessoryView.h */,
+ 60EFC05743E457629F6FA3BE /* SLKInputAccessoryView.m */,
+ CDD2299FFE62AC33DD5F6F21 /* SLKTextInputbar.h */,
+ 9FF5C109F0F229E7A27AE4D5 /* SLKTextInputbar.m */,
+ 7055B434E17A758334C4D690 /* SLKTextView.h */,
+ FAB2A620AECF49DF8EC027EB /* SLKTextView.m */,
+ 2630AA286F47284320CEDD6D /* SLKTextView+SLKAdditions.h */,
+ 0E88530B4D45296016366C45 /* SLKTextView+SLKAdditions.m */,
+ 1F0A7CDEDA02B55960040DCE /* SLKTextViewController.h */,
+ 1930DF3ED38A6053FBBC8322 /* SLKTextViewController.m */,
+ 1F3B904D76A0932EDC0DDC6C /* SLKTypingIndicatorView.h */,
+ 172242421980D777AB0996D9 /* SLKTypingIndicatorView.m */,
+ 784A38EF52EB47CE5E03AFEA /* SLKUIConstants.h */,
+ 9827A079F152A80A7F332466 /* UIResponder+SLKAdditions.h */,
+ 3ADF674E34301784035DA916 /* UIResponder+SLKAdditions.m */,
+ B66AACC28C7F958977E64248 /* UIScrollView+SLKAdditions.h */,
+ F294343CD38A6789868EF3AB /* UIScrollView+SLKAdditions.m */,
+ 201D11D9F140BA666D2ADC73 /* UIView+SLKAdditions.h */,
+ E91CE9439FF285A3B1918133 /* UIView+SLKAdditions.m */,
+ 04FBB9A3894582C5C79339A6 /* Support Files */,
+ );
+ path = SlackTextViewController;
+ sourceTree = "";
+ };
+ B0D42594583157D20C51E87F /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ F23F1F60BEAD735BCE6E238B /* iOS */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+ B1E0B2AD9C1688CCAC638A01 = {
+ isa = PBXGroup;
+ children = (
+ 3665C875F733140A55AD452B /* Podfile */,
+ B0D42594583157D20C51E87F /* Frameworks */,
+ D87B3D79ED4806259DDDFEF6 /* Pods */,
+ 427BECA3398D88D929354AB6 /* Products */,
+ 7DE09A3B7A1C6B5C8C44A7EF /* Targets Support Files */,
+ );
+ sourceTree = "";
+ };
+ D87B3D79ED4806259DDDFEF6 /* Pods */ = {
+ isa = PBXGroup;
+ children = (
+ 9CFAE8514509E6C3EF2B39B3 /* SlackTextViewController */,
+ );
+ name = Pods;
+ sourceTree = "";
+ };
+ F23F1F60BEAD735BCE6E238B /* iOS */ = {
+ isa = PBXGroup;
+ children = (
+ B41EF424A850AF9597402D7B /* Foundation.framework */,
+ );
+ name = iOS;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+ 03F7BD4F1B88344408E38DE6 /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ E8035C87DFD1E736BE9CCD9A /* SLKInputAccessoryView.h in Headers */,
+ 8F8598074DC5378DE846D9F7 /* SLKTextInputbar.h in Headers */,
+ A7A374D1CD4744F4774FAD99 /* SLKTextView+SLKAdditions.h in Headers */,
+ 16C7AE8DEA0077FB3D896BF0 /* SLKTextView.h in Headers */,
+ 42927E531994B740240AD1F8 /* SLKTextViewController.h in Headers */,
+ 8276AAEEBBAF4FEE31EEF70A /* SLKTypingIndicatorView.h in Headers */,
+ 32678D01710471A868EEF0AE /* SLKUIConstants.h in Headers */,
+ F1221A6E5E840FB484D12BFD /* UIResponder+SLKAdditions.h in Headers */,
+ FB64B1B87CE3B6BBF828598E /* UIScrollView+SLKAdditions.h in Headers */,
+ 32A92A492370904FDA401B29 /* UIView+SLKAdditions.h in Headers */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXNativeTarget section */
+ B54EC4326501540DDBBC9B97 /* Pods */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 4B82C4FC01672BAF03F56AB0 /* Build configuration list for PBXNativeTarget "Pods" */;
+ buildPhases = (
+ 7DE96EAA3669261071BAD3E4 /* Sources */,
+ FBE955E2F286A978C3471329 /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ B75877BB9EA23200B958B82E /* PBXTargetDependency */,
+ );
+ name = Pods;
+ productName = Pods;
+ productReference = 6591BD5CAF7C70A208E5C5DB /* libPods.a */;
+ productType = "com.apple.product-type.library.static";
+ };
+ BB9541CC3748C1191DEF8D25 /* Pods-SlackTextViewController */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = D7E02682B457EF6A6280F9AE /* Build configuration list for PBXNativeTarget "Pods-SlackTextViewController" */;
+ buildPhases = (
+ 2D95483827EDDA158405C5A3 /* Sources */,
+ C4247E842C0701BB7FF50710 /* Frameworks */,
+ 03F7BD4F1B88344408E38DE6 /* Headers */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = "Pods-SlackTextViewController";
+ productName = "Pods-SlackTextViewController";
+ productReference = 4449CD8C2533D423CEE6B594 /* libPods-SlackTextViewController.a */;
+ productType = "com.apple.product-type.library.static";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 8B26729A3C6DF0B6B83B55BB /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 0510;
+ };
+ buildConfigurationList = 8E9027938227B7173C82EA14 /* Build configuration list for PBXProject "Pods" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ );
+ mainGroup = B1E0B2AD9C1688CCAC638A01;
+ productRefGroup = 427BECA3398D88D929354AB6 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ B54EC4326501540DDBBC9B97 /* Pods */,
+ BB9541CC3748C1191DEF8D25 /* Pods-SlackTextViewController */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 2D95483827EDDA158405C5A3 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ A20C8CD4FA62A08EA32FB320 /* Pods-SlackTextViewController-dummy.m in Sources */,
+ 457D66B4156AA1908749B089 /* SLKInputAccessoryView.m in Sources */,
+ F3F121D9C25CBF2C8C5CB6FD /* SLKTextInputbar.m in Sources */,
+ BF4B84A5AA10198C93EF73AB /* SLKTextView+SLKAdditions.m in Sources */,
+ 5EC3EF97656A41D44CF363B4 /* SLKTextView.m in Sources */,
+ FA0F22AE0A83CE07B10A234A /* SLKTextViewController.m in Sources */,
+ A891E95D7B87B4E183738431 /* SLKTypingIndicatorView.m in Sources */,
+ DAB3B22291DFA7F6D080EE66 /* UIResponder+SLKAdditions.m in Sources */,
+ 07DA840C480DEF98FC6F0E0D /* UIScrollView+SLKAdditions.m in Sources */,
+ 63EEAE6BC1F6324007967880 /* UIView+SLKAdditions.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 7DE96EAA3669261071BAD3E4 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ B294EBBAAA27FB04D4FE8DE0 /* Pods-dummy.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ B75877BB9EA23200B958B82E /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ name = "Pods-SlackTextViewController";
+ target = BB9541CC3748C1191DEF8D25 /* Pods-SlackTextViewController */;
+ targetProxy = 542E4E1AA94F676B09AEE37B /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+ 2052550A748A76EA30695CE5 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 2ECB69507C18B14A1DD3CE5E /* Pods.debug.xcconfig */;
+ buildSettings = {
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ OTHER_LDFLAGS = "";
+ OTHER_LIBTOOLFLAGS = "";
+ PODS_ROOT = "$(SRCROOT)";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = iphoneos;
+ SKIP_INSTALL = YES;
+ };
+ name = Debug;
+ };
+ 26FB7D7741DD11D9B15ADF7F /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 383F6CE99248AAF4C3B60881 /* Pods.release.xcconfig */;
+ buildSettings = {
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ OTHER_LDFLAGS = "";
+ OTHER_LIBTOOLFLAGS = "";
+ PODS_ROOT = "$(SRCROOT)";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = iphoneos;
+ SKIP_INSTALL = YES;
+ };
+ name = Release;
+ };
+ 603F72B2BF325C2D14018DA6 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = YES;
+ ENABLE_NS_ASSERTIONS = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_PREPROCESSOR_DEFINITIONS = "RELEASE=1";
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ STRIP_INSTALLED_PRODUCT = NO;
+ SYMROOT = "${SRCROOT}/../build";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 940B952B180C96F7963E6571 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = B1C30DBE70E62F5C3FEF4944 /* Pods-SlackTextViewController-Private.xcconfig */;
+ buildSettings = {
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_PREFIX_HEADER = "Target Support Files/Pods-SlackTextViewController/Pods-SlackTextViewController-prefix.pch";
+ IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ OTHER_LDFLAGS = "";
+ OTHER_LIBTOOLFLAGS = "";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = iphoneos;
+ SKIP_INSTALL = YES;
+ };
+ name = Debug;
+ };
+ A18066350F539C6D0471BE6B /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = B1C30DBE70E62F5C3FEF4944 /* Pods-SlackTextViewController-Private.xcconfig */;
+ buildSettings = {
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_PREFIX_HEADER = "Target Support Files/Pods-SlackTextViewController/Pods-SlackTextViewController-prefix.pch";
+ IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ OTHER_LDFLAGS = "";
+ OTHER_LIBTOOLFLAGS = "";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = iphoneos;
+ SKIP_INSTALL = YES;
+ };
+ name = Release;
+ };
+ B1FB22DD2936B88C267CABCD /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ ONLY_ACTIVE_ARCH = YES;
+ STRIP_INSTALLED_PRODUCT = NO;
+ SYMROOT = "${SRCROOT}/../build";
+ };
+ name = Debug;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 4B82C4FC01672BAF03F56AB0 /* Build configuration list for PBXNativeTarget "Pods" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 2052550A748A76EA30695CE5 /* Debug */,
+ 26FB7D7741DD11D9B15ADF7F /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 8E9027938227B7173C82EA14 /* Build configuration list for PBXProject "Pods" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ B1FB22DD2936B88C267CABCD /* Debug */,
+ 603F72B2BF325C2D14018DA6 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ D7E02682B457EF6A6280F9AE /* Build configuration list for PBXNativeTarget "Pods-SlackTextViewController" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 940B952B180C96F7963E6571 /* Debug */,
+ A18066350F539C6D0471BE6B /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 8B26729A3C6DF0B6B83B55BB /* Project object */;
+}
diff --git a/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/LICENSE b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/LICENSE
new file mode 100644
index 000000000..5445d843e
--- /dev/null
+++ b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/LICENSE
@@ -0,0 +1,14 @@
+
+ Copyright 2015 Slack Technologies, Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/README.md b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/README.md
new file mode 100644
index 000000000..777d9ea0b
--- /dev/null
+++ b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/README.md
@@ -0,0 +1,324 @@
+#SlackTextViewController
+
+[](https://travis-ci.org/slackhq/SlackTextViewController)
+[](https://coveralls.io/r/slackhq/SlackTextViewController)
+
+[](https://cocoadocs.org/docsets/SlackTextViewController)
+[](https://github.com/Carthage/Carthage)
+[](http://opensource.org/licenses/Apache2.0)
+
+
+A drop-in UIViewController subclass with a growing text input view and other useful messaging features. Meant to be a replacement for UITableViewController & UICollectionViewController.
+
+
+
+This library is used in Slack's iOS app. It was built to fit our needs, but is flexible enough to be reused by others wanting to build great messaging apps for iOS.
+
+## Features
+
+### Core
+- Works out of the box with [UITableView or UICollectionView or UIScrollView](https://github.com/slackhq/SlackTextViewController/tree/swift-example#subclassing)
+- [Growing Text View](https://github.com/slackhq/SlackTextViewController#growing-text-view), with line count limit support
+- Flexible UI built with Auto Layout
+- Customizable: provides left and right button, and toolbar outlets
+- Tap Gesture for dismissing the keyboard
+- [Panning Gesture](https://github.com/slackhq/SlackTextViewController#panning-gesture) for sliding down the keyboard
+- [External keyboard](https://github.com/slackhq/SlackTextViewController#external-keyboard) commands support
+- Undo/Redo (with keyboard commands and UIMenuController)
+- Text Appending APIs
+
+### Optional
+- [Autocomplete Mode](https://github.com/slackhq/SlackTextViewController#autocompletion) by registering any prefix key (`@`, `#`, `/`)
+- [Edit Mode](https://github.com/slackhq/SlackTextViewController#edit-mode)
+- [Typing Indicator](https://github.com/slackhq/SlackTextViewController#typing-indicator) display
+- [Shake Gesture](https://github.com/slackhq/SlackTextViewController#shake-gesture) for clearing text view
+- Multimedia Pasting (png, gif, mov, etc.)
+- [Inverted Mode](https://github.com/slackhq/SlackTextViewController#inverted-mode) for displaying cells upside-down (using CATransform) -- a necessary hack for some messaging apps. `YES` by default, so beware, your entire cells might be flipped!
+- Bouncy Animations
+
+### Compatibility
+- Swift: [A sample project is available in a different branch] (https://github.com/slackhq/SlackTextViewController/tree/swift-example)
+- iOS 7 & 8
+- iPhone & iPad
+- [Storyboard](https://github.com/slackhq/SlackTextViewController#storyboard)
+- UIPopOverController & UITabBarController
+- Container View Controller
+- Auto-Rotation
+- Localization
+
+## Installation
+
+###### With [Cocoa Pods](http://cocoapods.org):
+```ruby
+pod 'SlackTextViewController'
+```
+
+###### With [Carthage](https://github.com/Carthage/Carthage):
+```
+github "slackhq/SlackTextViewController"
+```
+
+###### Manually:
+There are two ways to do this:
+- Copy and drag the `Source/` folder to your project.
+- or compile the project located in `Builder/SlackTextViewController.xcodeproj` to create a `SlackTextViewController.framework` package. You could also [link the library into your project](https://developer.apple.com/library/ios/recipes/xcode_help-project_editor/Articles/AddingaLibrarytoaTarget.html#//apple_ref/doc/uid/TP40010155-CH17-SW1).
+
+##How to use
+
+###Subclassing
+`SLKTextViewController` is meant to be subclassed, like you would normally do with UITableViewController or UICollectionViewController or UIScrollView. This pattern is a convenient way of extending UIViewController. SlackTextViewController manages a lot behind the scenes while still providing the ability to add custom behaviours. You may override methods, and decide to call super and perform additional logic, or not to call super and override default logic.
+
+Start by creating a new subclass of `SLKTextViewController`.
+
+In the init overriding method, if you wish to use the `UITableView` version, call:
+```objc
+[super initWithTableViewStyle:UITableViewStylePlain]
+```
+
+or the `UICollectionView` version:
+```objc
+[super initWithCollectionViewLayout:[UICollectionViewFlowLayout new]]
+```
+
+or the `UIScrollView` version:
+```objc
+[super initWithScrollView:self.myStrongScrollView]
+```
+
+
+Protocols like `UITableViewDelegate` and `UITableViewDataSource` are already setup for you. You will be able to call whatever delegate and data source methods you need for customising your control.
+
+Calling `[super init]` will call `[super initWithTableViewStyle:UITableViewStylePlain]` by default.
+
+
+###Growing Text View
+
+
+
+The text view expands automatically when a new line is required, until it reaches its `maxNumberOfLines`value. You may change this property's value in the textView.
+
+By default, the number of lines is set to best fit each device dimensions:
+- iPhone 4 (<=480pts): 4 lines
+- iPhone 5/6 (>=568pts): 6 lines
+- iPad (>=768pts): 8 lines
+
+On iPhone devices, in landscape orientation, the maximum number of lines is changed to fit the available space.
+
+###Autocompletion
+
+We use autocompletion for many things: names, channels, emoji, and more.
+
+
+
+To set up autocompletion in your app, follow these simple steps:
+
+#### 1. Registration
+You must first register all the prefixes you'd like to support for autocompletion detection:
+````objc
+[self registerPrefixesForAutoCompletion:@[@"#"]];
+````
+
+#### 2. Processing
+Every time a new character is inserted in the text view, the nearest word to the caret will be processed and verified if it contains any of the registered prefixes.
+
+Once the prefix has been detected, `-canShowAutoCompletion` will be called. This is the perfect place to populate your data source, and return a BOOL if the autocompletion view should actually be shown. So you must override it in your subclass, to be able to perform additional tasks. Default returns NO.
+
+````objc
+- (BOOL)canShowAutoCompletion
+{
+ NSString *prefix = self.foundPrefix;
+ NSString *word = self.foundWord;
+
+ self.searchResult = [[NSArray alloc] initWithArray:self.channels];
+
+ if ([prefix isEqualToString:@"#"])
+ {
+ if (word.length > 0) {
+ self.searchResult = [self.searchResult filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"self BEGINSWITH[c] %@ AND self !=[c] %@", word, word]];
+ }
+ }
+
+ if (self.searchResult.count > 0) {
+ self.searchResult = [self.searchResult sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
+ }
+
+ return self.searchResult.count > 0;
+}
+````
+
+The autocompletion view is a `UITableView` instance, so you will need to use `UITableViewDataSource` to populate its cells. You have complete freedom for customizing the cells.
+
+You don't need to call `-reloadData` yourself, since it will be called automatically if you return `YES` in `-canShowAutoCompletion` method.
+
+#### 3. Layout
+
+The maximum height of the autocompletion view is set to 140 pts by default. You can update this value anytime, so the view automatically adjusts based on the amount of displayed cells.
+
+````objc
+- (CGFloat)heightForAutoCompletionView
+{
+ CGFloat cellHeight = 34.0;
+ return cellHeight*self.searchResult.count;
+}
+````
+
+#### 4. Confirmation
+
+If the user selects any autocompletion view cell on `-tableView:didSelectRowAtIndexPath:`, you must call `-acceptAutoCompletionWithString:` to commit autocompletion. That method expects a string matching the selected item, that you would like to be inserted in the text view.
+
+`````objc
+- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
+{
+ if ([tableView isEqual:self.autoCompletionView]) {
+
+ NSString *item = self.searchResult[indexPath.row];
+
+ [self acceptAutoCompletionWithString:item];
+ }
+}
+````
+
+The autocompletion view will automatically be dismissed and the chosen string will be inserted in the text view, replacing the detected prefix and word.
+
+You can always call `-cancelAutoCompletion` to exit the autocompletion mode and refresh the UI.
+
+
+###Edit Mode
+
+
+
+To enable edit mode, you simply need to call `[self editText:@"hello"];`, and the text input will switch to edit mode, removing both left and right buttons, extending the input bar a bit higher with "Accept" and "Cancel" buttons. Both of this buttons are accessible in the `SLKTextInputbar` instance for customisation.
+
+To capture the "Accept" or "Cancel" events, you must override the following methods.
+
+````objc
+- (void)didCommitTextEditing:(id)sender
+{
+ NSString *message = [self.textView.text copy];
+
+ [self.messages removeObjectAtIndex:0];
+ [self.messages insertObject:message atIndex:0];
+ [self.tableView reloadData];
+
+ [super didCommitTextEditing:sender];
+}
+
+- (void)didCancelTextEditing:(id)sender
+{
+ [super didCancelTextEditing:sender];
+}
+````
+
+Notice that you must call `super` at some point, so the text input exits the edit mode, re-adjusting the layout and clearing the text view.
+Use the `editing` property to know if the editing mode is on.
+
+
+###Typing Indicator
+
+
+
+Optionally, you can enable a simple typing indicator, which will be displayed right above the text input. It shows the name of the people that are typing, and if more than 2, it will display "Several are typing" message.
+
+To enable the typing indicator, just call `[self.typingIndicatorView insertUsername:@"John"];` and the view will automatically be animated on top of the text input. After a default interval of 6 seconds, if the same name hasn't been assigned once more, the view will be dismissed with animation.
+
+You can remove names from the list by calling `[self.typingIndicatorView removeUsername:@"John"];`
+
+You can also dismiss it by calling `[self.typingIndicatorView dismissIndicator];`
+
+###Panning Gesture
+
+Dismissing the keyboard with a panning gesture is enabled by default with the `keyboardPanningEnabled` property. You can always disable it if you'd like.
+
+###Shake Gesture
+
+
+
+A shake gesture to clear text is enabled by default with the `undoShakingEnabled` property.
+
+You can optionally override `-willRequestUndo`, to implement your UI to ask the users if he would like to clean the text view's text. If there is not text entered, the method will not be called.
+
+If you don't override `-willRequestUndo` and `undoShakingEnabled` is set to `YES`, a system UIAlertView will prompt.
+
+###Inverted Mode
+
+Some UITableView layouts may require that new messages enter from bottom to top. To enable this, you must use the `inverted` flag property. This will actually invert the UITableView or UICollectionView, so you will need to do a transform adjustment in your UITableViewDataSource method `-tableView:cellForRowAtIndexPath:` for the cells to show correctly.
+
+````objc
+- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
+{
+ UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:chatCellIdentifier];
+
+ // Cells must inherit the table view's transform
+ // This is very important, since the main table view may be inverted
+ cell.transform = self.tableView.transform;
+}
+````
+
+###External Keyboard
+
+There a few basic key commands enabled by default:
+- cmd + z -> undo
+- shift + cmd + z -> redo
+- return key -> calls `-didPressRightButton:`, or `-didCommitTextEditing:` if in edit mode
+- shift/cmd + return key -> line break
+- escape key -> exits edit mode, or auto-completion mode, or dismisses the keyboard
+- up & down arrows -> vertical cursor movement
+
+To add additional key commands, simply override `-keyCommands` and append `super`'s array.
+
+`````objc
+- (NSArray *)keyCommands
+{
+ NSMutableArray *commands = [NSMutableArray arrayWithArray:[super keyCommands]];
+
+ // Edit last message
+ [commands addObject:[UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow
+ modifierFlags:0
+ action:@selector(editLastMessage:)]];
+
+ return commands;
+}
+````
+
+##Storyboard
+
+When using SlackTextViewController with storyboards, instead of overriding the traditional `initWithCoder:` you will need to override any of the two custom methods below. This approach helps preserving the exact same features from the programatic approach, but also limits the edition of the nib of your `SLKTextViewController` subclass since it doesn't layout subviews from the nib (subviews are still initialized and layed out programatically).
+
+if you wish to use the `UITableView` version, call:
+```objc
++ (UITableViewStyle)tableViewStyleForCoder:(NSCoder *)decoder
+{
+ return UITableViewStylePlain;
+}
+```
+
+or the `UICollectionView` version:
+```objc
++ (UICollectionViewLayout *)collectionViewLayoutForCoder:(NSCoder *)decoder
+{
+ return [UICollectionViewFlowLayout new];
+}
+```
+
+
+##Sample Project
+
+Check out the sample project, everything is demo'd there.
+There are 2 main examples (different targets) for testing the programatic and storyboard approaches.
+
+A CollectionView example, using Swift, is in progress on the `swift-example` branch. The idea with this project is to build a custom collection view layout allowing to display cells from the bottom (currently working but needs serious tweaks to make it perfect).
+Feel free to contribute!
+
+
+##XCode Templates
+
+
+
+We have prepared a set of useful XCode templates so you can quickly start using SlackTextViewController.
+
+To install them, open up your terminal and type:
+```bash
+sh ./SlackTextViewController/File\ Templates/install.sh
+```
+
+These templates are also available in [Alcatraz](https://github.com/supermarin/Alcatraz).
diff --git a/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Additions/SLKTextView+SLKAdditions.h b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Additions/SLKTextView+SLKAdditions.h
new file mode 100644
index 000000000..13a058323
--- /dev/null
+++ b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Additions/SLKTextView+SLKAdditions.h
@@ -0,0 +1,80 @@
+//
+// Copyright 2014 Slack Technologies, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import "SLKTextView.h"
+
+/** @name SLKTextView additional features used for SlackTextViewController. */
+@interface SLKTextView (SLKAdditions)
+
+/**
+ Clears the text.
+
+ @param clearUndo YES if clearing the text should also clear the undo manager (if enabled).
+ */
+- (void)slk_clearText:(BOOL)clearUndo;
+
+/**
+ Scrolls to the very end of the content size, animated.
+
+ @param animated YES if the scrolling should be animated.
+ */
+- (void)slk_scrollToBottomAnimated:(BOOL)animated;
+
+/**
+ Scrolls to the caret position, animated.
+
+ @param animated YES if the scrolling should be animated.
+ */
+- (void)slk_scrollToCaretPositonAnimated:(BOOL)animated;
+
+/**
+ Inserts a line break at the caret's position.
+ */
+- (void)slk_insertNewLineBreak;
+
+/**
+ Inserts a string at the caret's position.
+
+ @param text The string to be appended to the current text.
+ */
+- (void)slk_insertTextAtCaretRange:(NSString *)text;
+
+/**
+ Adds a string to a specific range.
+
+ @param text The string to be appended to the current text.
+ @param range The range where to insert text.
+
+ @return The range of the newly inserted text.
+ */
+- (NSRange)slk_insertText:(NSString *)text inRange:(NSRange)range;
+
+/**
+ Finds the word close to the caret's position, if any.
+
+ @param range Returns the range of the found word.
+ @returns The found word.
+ */
+- (NSString *)slk_wordAtCaretRange:(NSRangePointer)range;
+
+/**
+ Registers the current text for future undo actions.
+
+ @param description A simple description associated with the Undo or Redo command.
+ */
+- (void)slk_prepareForUndo:(NSString *)description;
+
+@end
diff --git a/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Additions/SLKTextView+SLKAdditions.m b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Additions/SLKTextView+SLKAdditions.m
new file mode 100644
index 000000000..03826d242
--- /dev/null
+++ b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Additions/SLKTextView+SLKAdditions.m
@@ -0,0 +1,170 @@
+//
+// Copyright 2014 Slack Technologies, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import "SLKTextView+SLKAdditions.h"
+
+@implementation SLKTextView (SLKAdditions)
+
+- (void)slk_clearText:(BOOL)clearUndo
+{
+ // Important to call self implementation, as SLKTextView overrides setText: to add additional features.
+ [self setText:nil];
+
+ if (self.undoManagerEnabled && clearUndo) {
+ [self.undoManager removeAllActions];
+ }
+}
+
+- (void)slk_scrollToCaretPositonAnimated:(BOOL)animated
+{
+ if (animated) {
+ [self scrollRangeToVisible:self.selectedRange];
+ }
+ else {
+ [UIView performWithoutAnimation:^{
+ [self scrollRangeToVisible:self.selectedRange];
+ }];
+ }
+}
+
+- (void)slk_scrollToBottomAnimated:(BOOL)animated
+{
+ CGRect rect = [self caretRectForPosition:self.selectedTextRange.end];
+ rect.size.height += self.textContainerInset.bottom;
+
+ if (animated) {
+ [self scrollRectToVisible:rect animated:animated];
+ }
+ else {
+ [UIView performWithoutAnimation:^{
+ [self scrollRectToVisible:rect animated:NO];
+ }];
+ }
+}
+
+- (void)slk_insertNewLineBreak
+{
+ [self slk_insertTextAtCaretRange:@"\n"];
+
+ // if the text view cannot expand anymore, scrolling to bottom are not animated to fix a UITextView issue scrolling twice.
+ BOOL animated = !self.isExpanding;
+
+ //Detected break. Should scroll to bottom if needed.
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.0125 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+ [self slk_scrollToBottomAnimated:animated];
+ });
+}
+
+- (void)slk_insertTextAtCaretRange:(NSString *)text
+{
+ NSRange range = [self slk_insertText:text inRange:self.selectedRange];
+ self.selectedRange = NSMakeRange(range.location, 0);
+}
+
+- (NSRange)slk_insertText:(NSString *)text inRange:(NSRange)range
+{
+ // Skip if the text is empty
+ if (text.length == 0) {
+ return NSMakeRange(0, 0);
+ }
+
+ // Registers for undo management
+ [self slk_prepareForUndo:@"Text appending"];
+
+ // Append the new string at the caret position
+ if (range.length == 0)
+ {
+ NSString *leftString = [self.text substringToIndex:range.location];
+ NSString *rightString = [self.text substringFromIndex: range.location];
+
+ self.text = [NSString stringWithFormat:@"%@%@%@", leftString, text, rightString];
+
+ range.location += [text length];
+ return range;
+ }
+ // Some text is selected, so we replace it with the new text
+ else if (range.location != NSNotFound && range.length > 0)
+ {
+ self.text = [self.text stringByReplacingCharactersInRange:range withString:text];
+
+ return NSMakeRange(range.location+[self.text rangeOfString:text].length, text.length);
+ }
+
+ // No text has been inserted, but still return the caret range
+ return self.selectedRange;
+}
+
+- (NSString *)slk_wordAtCaretRange:(NSRangePointer)range
+{
+ return [self slk_wordAtRange:self.selectedRange rangeInText:range];
+}
+
+- (NSString *)slk_wordAtRange:(NSRange)range rangeInText:(NSRangePointer)rangePointer
+{
+ NSString *text = self.text;
+ NSInteger location = range.location;
+
+ // Aborts in case minimum requieres are not fufilled
+ if (text.length == 0 || location < 0 || (range.location+range.length) > text.length) {
+ *rangePointer = NSMakeRange(0, 0);
+ return nil;
+ }
+
+ NSString *leftPortion = [text substringToIndex:location];
+ NSArray *leftComponents = [leftPortion componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
+ NSString *leftWordPart = [leftComponents lastObject];
+
+ NSString *rightPortion = [text substringFromIndex:location];
+ NSArray *rightComponents = [rightPortion componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
+ NSString *rightPart = [rightComponents firstObject];
+
+ if (location > 0) {
+ NSString *characterBeforeCursor = [text substringWithRange:NSMakeRange(location-1, 1)];
+
+ if ([characterBeforeCursor isEqualToString:@" "]) {
+ // At the start of a word, just use the word behind the cursor for the current word
+ *rangePointer = NSMakeRange(location, rightPart.length);
+
+ return rightPart;
+ }
+ }
+
+ // In the middle of a word, so combine the part of the word before the cursor, and after the cursor to get the current word
+ *rangePointer = NSMakeRange(location-leftWordPart.length, leftWordPart.length+rightPart.length);
+ NSString *word = [leftWordPart stringByAppendingString:rightPart];
+
+ // If a break is detected, return the last component of the string
+ if ([word rangeOfString:@"\n"].location != NSNotFound) {
+ *rangePointer = [text rangeOfString:word];
+ word = [[word componentsSeparatedByString:@"\n"] lastObject];
+ }
+
+ return word;
+}
+
+- (void)slk_prepareForUndo:(NSString *)description
+{
+ if (!self.undoManagerEnabled) {
+ return;
+ }
+
+ SLKTextView *prepareInvocation = [self.undoManager prepareWithInvocationTarget:self];
+ [prepareInvocation setText:self.text];
+ [self.undoManager setActionName:description];
+
+}
+
+@end
diff --git a/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Additions/SLKUIConstants.h b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Additions/SLKUIConstants.h
new file mode 100644
index 000000000..f65ac47f4
--- /dev/null
+++ b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Additions/SLKUIConstants.h
@@ -0,0 +1,61 @@
+//
+// Copyright 2014 Slack Technologies, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#define SLK_IS_LANDSCAPE ([[UIApplication sharedApplication] statusBarOrientation] == UIDeviceOrientationLandscapeLeft || [[UIApplication sharedApplication] statusBarOrientation] == UIDeviceOrientationLandscapeRight)
+#define SLK_IS_IPAD ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
+#define SLK_IS_IPHONE ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)
+#define SLK_IS_IPHONE4 (SLK_IS_IPHONE && [[UIScreen mainScreen] bounds].size.height < 568.0)
+#define SLK_IS_IPHONE5 (SLK_IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 568.0)
+#define SLK_IS_IPHONE6 (SLK_IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 667.0)
+#define SLK_IS_IPHONE6PLUS (SLK_IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 736.0 || [[UIScreen mainScreen] bounds].size.width == 736.0) // Both orientations
+#define SLK_IS_IOS8_AND_HIGHER ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0)
+
+#define SLK_INPUT_ACCESSORY_DEBUG DEBUG && 0 // Renders a translucent red area representing the keyboard accessory view
+#define SLK_KEYBOARD_NOTIFICATION_DEBUG DEBUG && 0 // Logs every keyboard notification being sent
+
+#if __has_attribute(objc_designated_initializer)
+#define SLK_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer))
+#endif
+
+static NSString *SLKTextViewControllerDomain = @"com.slack.TextViewController";
+
+inline static CGFloat minimumKeyboardHeight()
+{
+ if (SLK_IS_IPAD) {
+ if (SLK_IS_LANDSCAPE) return 352.f;
+ else return 264.f;
+ }
+ if (SLK_IS_IPHONE6PLUS) {
+ if (SLK_IS_LANDSCAPE) return 162.f;
+ else return 226.f;
+ }
+ else {
+ if (SLK_IS_LANDSCAPE) return 162.f;
+ else return 216.f;
+ }
+}
+
+inline static CGRect SLKRectInvert(CGRect rect)
+{
+ CGRect invert = CGRectZero;
+
+ invert.origin.x = rect.origin.y;
+ invert.origin.y = rect.origin.x;
+ invert.size.width = rect.size.height;
+ invert.size.height = rect.size.width;
+
+ return invert;
+}
\ No newline at end of file
diff --git a/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Additions/UIResponder+SLKAdditions.h b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Additions/UIResponder+SLKAdditions.h
new file mode 100644
index 000000000..3b126b2e5
--- /dev/null
+++ b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Additions/UIResponder+SLKAdditions.h
@@ -0,0 +1,30 @@
+//
+// Copyright 2014 Slack Technologies, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import
+
+/** @name UIResponder additional features used for SlackTextViewController. */
+@interface UIResponder (SLKAdditions)
+
+/**
+ Returns the current first responder object.
+ @discussion This is an experimental API. Use it at your own risk.
+
+ @return A UIResponder instance.
+ */
++ (instancetype)slk_currentFirstResponder;
+
+@end
diff --git a/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Additions/UIResponder+SLKAdditions.m b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Additions/UIResponder+SLKAdditions.m
new file mode 100644
index 000000000..799aba254
--- /dev/null
+++ b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Additions/UIResponder+SLKAdditions.m
@@ -0,0 +1,39 @@
+//
+// Copyright 2014 Slack Technologies, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import "UIResponder+SLKAdditions.h"
+
+static __weak id ___currentFirstResponder;
+
+@implementation UIResponder (SLKAdditions)
+
+/**
+ Based on Jakob Egger's answer in http://stackoverflow.com/a/14135456/590010
+ */
++ (instancetype)slk_currentFirstResponder
+{
+ ___currentFirstResponder = nil;
+ [[UIApplication sharedApplication] sendAction:@selector(slk_findFirstResponder:) to:nil from:nil forEvent:nil];
+
+ return ___currentFirstResponder;
+}
+
+- (void)slk_findFirstResponder:(id)sender
+{
+ ___currentFirstResponder = self;
+}
+
+@end
diff --git a/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Additions/UIScrollView+SLKAdditions.h b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Additions/UIScrollView+SLKAdditions.h
new file mode 100644
index 000000000..cc33a631c
--- /dev/null
+++ b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Additions/UIScrollView+SLKAdditions.h
@@ -0,0 +1,48 @@
+//
+// Copyright 2014 Slack Technologies, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import
+
+/** @name UIScrollView additional features used for SlackTextViewController. */
+@interface UIScrollView (SLKAdditions)
+
+/** YES if the scrollView's offset is at the very top. */
+@property (nonatomic, readonly) BOOL slk_isAtTop;
+/** YES if the scrollView's offset is at the very bottom. */
+@property (nonatomic, readonly) BOOL slk_isAtBottom;
+/** The visible area of the content size. */
+@property (nonatomic, readonly) CGRect slk_visibleRect;
+
+/**
+ Sets the content offset to the top.
+
+ @param animated YES to animate the transition at a constant velocity to the new offset, NO to make the transition immediate.
+ */
+- (void)slk_scrollToTopAnimated:(BOOL)animated;
+
+/**
+ Sets the content offset to the bottom.
+
+ @param animated YES to animate the transition at a constant velocity to the new offset, NO to make the transition immediate.
+ */
+- (void)slk_scrollToBottomAnimated:(BOOL)animated;
+
+/**
+ Stops scrolling, if it was scrolling.
+ */
+- (void)slk_stopScrolling;
+
+@end
diff --git a/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Additions/UIScrollView+SLKAdditions.m b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Additions/UIScrollView+SLKAdditions.m
new file mode 100644
index 000000000..2c745e412
--- /dev/null
+++ b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Additions/UIScrollView+SLKAdditions.m
@@ -0,0 +1,73 @@
+//
+// Copyright 2014 Slack Technologies, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import "UIScrollView+SLKAdditions.h"
+
+@implementation UIScrollView (SLKAdditions)
+
+- (void)slk_scrollToTopAnimated:(BOOL)animated
+{
+ [self scrollRectToVisible:[self slk_topRect] animated:animated];
+}
+
+- (void)slk_scrollToBottomAnimated:(BOOL)animated
+{
+ [self scrollRectToVisible:[self slk_bottomRect] animated:animated];
+}
+
+- (BOOL)slk_isAtTop
+{
+ return CGRectGetMinY([self slk_visibleRect]) == CGRectGetMinY([self slk_topRect]);
+}
+
+- (BOOL)slk_isAtBottom
+{
+ return CGRectGetMaxY([self slk_visibleRect]) == CGRectGetMaxY([self slk_bottomRect]);
+}
+
+- (CGRect)slk_visibleRect
+{
+ CGRect visibleRect;
+ visibleRect.origin = self.contentOffset;
+ visibleRect.size = self.frame.size;
+ return visibleRect;
+}
+
+- (CGRect)slk_topRect
+{
+ return CGRectMake(0, 0, CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds));
+}
+
+- (CGRect)slk_bottomRect
+{
+ return CGRectMake(0, self.contentSize.height - CGRectGetHeight(self.bounds), CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds));
+}
+
+- (void)slk_stopScrolling
+{
+ if (!self.isDragging) {
+ return;
+ }
+
+ CGPoint offset = self.contentOffset;
+ offset.y -= 1.0;
+ [self setContentOffset:offset];
+
+ offset.y += 1.0;
+ [self setContentOffset:offset];
+}
+
+@end
diff --git a/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Additions/UIView+SLKAdditions.h b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Additions/UIView+SLKAdditions.h
new file mode 100644
index 000000000..40c609608
--- /dev/null
+++ b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Additions/UIView+SLKAdditions.h
@@ -0,0 +1,59 @@
+//
+// Copyright 2014 Slack Technologies, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import
+
+/** @name UIView additional features used for SlackTextViewController. */
+@interface UIView (SLKAdditions)
+
+/**
+ Animates the view's constraints by calling layoutIfNeeded.
+
+ @param bounce YES if the animation should use spring damping and velocity to give a bouncy effect to animations.
+ @param options A mask of options indicating how you want to perform the animations.
+ @param animations An additional block for custom animations.
+ */
+- (void)slk_animateLayoutIfNeededWithBounce:(BOOL)bounce options:(UIViewAnimationOptions)options animations:(void (^)(void))animations;
+
+/**
+ Animates the view's constraints by calling layoutIfNeeded.
+
+ @param duration The total duration of the animations, measured in seconds.
+ @param bounce YES if the animation should use spring damping and velocity to give a bouncy effect to animations.
+ @param options A mask of options indicating how you want to perform the animations.
+ @param animations An additional block for custom animations.
+ */
+- (void)slk_animateLayoutIfNeededWithDuration:(NSTimeInterval)duration bounce:(BOOL)bounce options:(UIViewAnimationOptions)options animations:(void (^)(void))animations;
+
+/**
+ Returns the view constraints matching a specific layout attribute (top, bottom, left, right, leading, trailing, etc.)
+
+ @param attribute The layout attribute to use for searching.
+ @return An array of matching constraints.
+ */
+- (NSArray *)slk_constraintsForAttribute:(NSLayoutAttribute)attribute;
+
+/**
+ Returns a layout constraint matching a specific layout attribute and relationship between 2 items, first and second items.
+
+ @param attribute The layout attribute to use for searching.
+ @param first The first item in the relationship.
+ @param second The second item in the relationship.
+ @return A layout constraint.
+ */
+- (NSLayoutConstraint *)slk_constraintForAttribute:(NSLayoutAttribute)attribute firstItem:(id)first secondItem:(id)second;
+
+@end
diff --git a/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Additions/UIView+SLKAdditions.m b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Additions/UIView+SLKAdditions.m
new file mode 100644
index 000000000..b82f9435d
--- /dev/null
+++ b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Additions/UIView+SLKAdditions.m
@@ -0,0 +1,73 @@
+//
+// Copyright 2014 Slack Technologies, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import "UIView+SLKAdditions.h"
+
+#import "SLKUIConstants.h"
+
+@implementation UIView (SLKAdditions)
+
+- (void)slk_animateLayoutIfNeededWithBounce:(BOOL)bounce options:(UIViewAnimationOptions)options animations:(void (^)(void))animations
+{
+ NSTimeInterval duration = bounce ? 0.5 : 0.2;
+ [self slk_animateLayoutIfNeededWithDuration:duration bounce:bounce options:options animations:animations];
+}
+
+- (void)slk_animateLayoutIfNeededWithDuration:(NSTimeInterval)duration bounce:(BOOL)bounce options:(UIViewAnimationOptions)options animations:(void (^)(void))animations
+{
+ if (bounce) {
+ [UIView animateWithDuration:duration
+ delay:0.0
+ usingSpringWithDamping:0.7
+ initialSpringVelocity:0.7
+ options:options
+ animations:^{
+ [self layoutIfNeeded];
+
+ if (animations) {
+ animations();
+ }
+ }
+ completion:NULL];
+ }
+ else {
+ [UIView animateWithDuration:duration
+ delay:0.0
+ options:options
+ animations:^{
+ [self layoutIfNeeded];
+
+ if (animations) {
+ animations();
+ }
+ }
+ completion:NULL];
+ }
+}
+
+- (NSArray *)slk_constraintsForAttribute:(NSLayoutAttribute)attribute
+{
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"firstAttribute = %d", attribute];
+ return [self.constraints filteredArrayUsingPredicate:predicate];
+}
+
+- (NSLayoutConstraint *)slk_constraintForAttribute:(NSLayoutAttribute)attribute firstItem:(id)first secondItem:(id)second
+{
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"firstAttribute = %d AND firstItem = %@ AND secondItem = %@", attribute, first, second];
+ return [[self.constraints filteredArrayUsingPredicate:predicate] firstObject];
+}
+
+@end
diff --git a/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Classes/SLKInputAccessoryView.h b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Classes/SLKInputAccessoryView.h
new file mode 100644
index 000000000..f72fd105c
--- /dev/null
+++ b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Classes/SLKInputAccessoryView.h
@@ -0,0 +1,23 @@
+//
+// Copyright 2014 Slack Technologies, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import
+
+UIKIT_EXTERN NSString * const SLKInputAccessoryViewKeyboardFrameDidChangeNotification;
+
+@interface SLKInputAccessoryView : UIView
+
+@end
diff --git a/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Classes/SLKInputAccessoryView.m b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Classes/SLKInputAccessoryView.m
new file mode 100644
index 000000000..e1a4cd561
--- /dev/null
+++ b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Classes/SLKInputAccessoryView.m
@@ -0,0 +1,100 @@
+//
+// Copyright 2014 Slack Technologies, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import "SLKInputAccessoryView.h"
+#import "SLKUIConstants.h"
+
+NSString * const SLKInputAccessoryViewKeyboardFrameDidChangeNotification = @"SLKInputAccessoryViewKeyboardFrameDidChangeNotification";
+
+@interface SLKInputAccessoryView ()
+@property (nonatomic, weak) UIView *observedSuperview;
+@end
+
+@implementation SLKInputAccessoryView
+
+#pragma mark - Getters
+
+NSString *SLKKeyboardHandlingKeyPath()
+{
+ // Listening for the superview's frame doesn't work on iOS8 and above, so we use its center
+ if (SLK_IS_IOS8_AND_HIGHER) {
+ return NSStringFromSelector(@selector(center));
+ }
+ else {
+ return NSStringFromSelector(@selector(frame));
+ }
+}
+
+
+#pragma mark - Super Overrides
+
+- (void)willMoveToSuperview:(UIView *)newSuperview
+{
+ [self slk_removeSuperviewObserver];
+ [self slk_addSuperviewObserver:newSuperview];
+
+ [super willMoveToSuperview:newSuperview];
+}
+
+
+#pragma mark - Superview handling
+
+- (void)slk_addSuperviewObserver:(UIView *)superview
+{
+ if (!_observedSuperview && superview) {
+ _observedSuperview = superview;
+ [superview addObserver:self forKeyPath:SLKKeyboardHandlingKeyPath() options:0 context:NULL];
+ }
+}
+
+- (void)slk_removeSuperviewObserver
+{
+ if (_observedSuperview) {
+ [self.observedSuperview removeObserver:self forKeyPath:SLKKeyboardHandlingKeyPath()];
+ _observedSuperview = nil;
+ }
+}
+
+
+#pragma mark - Events
+
+- (void)slk_didChangeKeyboardFrame:(CGRect)frame
+{
+ NSDictionary *userInfo = @{UIKeyboardFrameEndUserInfoKey:[NSValue valueWithCGRect:frame]};
+ [[NSNotificationCenter defaultCenter] postNotificationName:SLKInputAccessoryViewKeyboardFrameDidChangeNotification object:nil userInfo:userInfo];
+}
+
+
+#pragma mark - KVO Listener
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
+{
+ if ([object isEqual:self.superview] && [keyPath isEqualToString:SLKKeyboardHandlingKeyPath()]) {
+ [self slk_didChangeKeyboardFrame:self.superview.frame];
+ }
+ else {
+ [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
+ }
+}
+
+#pragma mark - Lifeterm
+
+- (void)dealloc
+{
+ [self slk_removeSuperviewObserver];
+}
+
+@end
diff --git a/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Classes/SLKTextInputbar.h b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Classes/SLKTextInputbar.h
new file mode 100644
index 000000000..d179912d9
--- /dev/null
+++ b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Classes/SLKTextInputbar.h
@@ -0,0 +1,149 @@
+//
+// Copyright 2014 Slack Technologies, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import
+
+@class SLKTextViewController;
+@class SLKTextView;
+
+typedef NS_ENUM(NSUInteger, SLKCounterStyle) {
+ SLKCounterStyleNone,
+ SLKCounterStyleSplit,
+ SLKCounterStyleCountdown,
+ SLKCounterStyleCountdownReversed
+};
+
+typedef NS_ENUM(NSUInteger, SLKCounterPosition) {
+ SLKCounterPositionTop,
+ SLKCounterPositionBottom
+};
+
+/** @name A custom tool bar encapsulating messaging controls. */
+@interface SLKTextInputbar : UIToolbar
+
+/** A weak reference to the core view controller. */
+@property (nonatomic, weak) SLKTextViewController *controller;
+
+/** The centered text input view.
+ The maximum number of lines is configured by default, to best fit each devices dimensions.
+ For iPhone 4 (<=480pts): 4 lines
+ For iPhone 5 & 6 (>=568pts): 6 lines
+ For iPad (>=768pts): 8 lines
+ */
+@property (nonatomic, strong) SLKTextView *textView;
+
+/** The left action button action. */
+@property (nonatomic, strong) UIButton *leftButton;
+
+/** The right action button action. */
+@property (nonatomic, strong) UIButton *rightButton;
+
+/** YES if the right button should be hidden animatedly in case the text view has no text in it. Default is YES. */
+@property (nonatomic, readwrite) BOOL autoHideRightButton;
+
+/** The inner padding to use when laying out content in the view. Default is {5, 8, 5, 8}. */
+@property (nonatomic, assign) UIEdgeInsets contentInset;
+
+/** The minimum height based on the intrinsic content size's. */
+@property (nonatomic, readonly) CGFloat minimumInputbarHeight;
+
+/** The most appropriate height calculated based on the amount of lines of text and other factors. */
+@property (nonatomic, readonly) CGFloat appropriateHeight;
+
+
+#pragma mark - Initialization
+///------------------------------------------------
+/// @name Initialization
+///------------------------------------------------
+
+/**
+ Initializes a text input bar with a class to be used for the text view
+
+ @param textViewClass The class to be used when creating the text view. May be nil. If provided, the class must be a subclass of SLKTextView
+ @return An initialized SLKTextInputbar object or nil if the object could not be created.
+ */
+- (instancetype)initWithTextViewClass:(Class)textViewClass;
+
+
+#pragma mark - Text Editing
+///------------------------------------------------
+/// @name Text Editing
+///------------------------------------------------
+
+/** The view displayed on top if the text input bar, containing the button outlets, when editing is enabled. */
+@property (nonatomic, strong) UIView *editorContentView;
+
+/** The title label displayed in the middle of the accessoryView. */
+@property (nonatomic, strong) UILabel *editorTitle;
+
+/** The 'cancel' button displayed left in the accessoryView. */
+@property (nonatomic, strong) UIButton *editortLeftButton;
+
+/** The 'accept' button displayed right in the accessoryView. */
+@property (nonatomic, strong) UIButton *editortRightButton;
+
+/** The accessory view's maximum height. Default is 38 pts. */
+@property (nonatomic, assign) CGFloat editorContentViewHeight;
+
+/** A Boolean value indicating whether the control is in edit mode. */
+@property (nonatomic, getter = isEditing) BOOL editing;
+
+/**
+ Verifies if the text can be edited.
+
+ @param text The text to be edited.
+ @return YES if the text is editable.
+ */
+- (BOOL)canEditText:(NSString *)text;
+
+/**
+ Begins editing the text, by updating the 'editing' flag and the view constraints.
+ */
+- (void)beginTextEditing;
+
+/**
+ End editing the text, by updating the 'editing' flag and the view constraints.
+ */
+- (void)endTextEdition;
+
+
+#pragma mark - Text Counting
+///------------------------------------------------
+/// @name Text Counting
+///------------------------------------------------
+
+/** The label used to display the character counts. */
+@property (nonatomic, readonly) UILabel *charCountLabel;
+
+/** The maximum character count allowed. If larger than 0, a character count label will be displayed on top of the right button. Default is 0, which means limitless.*/
+@property (nonatomic, readwrite) NSUInteger maxCharCount;
+
+/** The character counter formatting. Ignored if maxCharCount is 0. Default is None. */
+@property (nonatomic, assign) SLKCounterStyle counterStyle;
+
+/** The character counter layout style. Ignored if maxCharCount is 0. Default is SLKCounterPositionTop. */
+@property (nonatomic, assign) SLKCounterPosition counterPosition;
+
+/** YES if the maxmimum character count has been exceeded. */
+@property (nonatomic, readonly) BOOL limitExceeded;
+
+/** The normal color used for character counter label. Default is lightGrayColor. */
+@property (nonatomic, strong, readwrite) UIColor *charCountLabelNormalColor;
+
+/** The color used for character counter label when it has exceeded the limit. Default is redColor. */
+@property (nonatomic, strong, readwrite) UIColor *charCountLabelWarningColor;
+
+@end
diff --git a/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Classes/SLKTextInputbar.m b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Classes/SLKTextInputbar.m
new file mode 100644
index 000000000..7e5f66ec8
--- /dev/null
+++ b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Classes/SLKTextInputbar.m
@@ -0,0 +1,707 @@
+//
+// Copyright 2014 Slack Technologies, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import "SLKTextInputbar.h"
+#import "SLKTextViewController.h"
+#import "SLKTextView.h"
+#import "SLKTextView+SLKAdditions.h"
+#import "SLKUIConstants.h"
+#import "UIView+SLKAdditions.h"
+
+@interface SLKTextInputbar ()
+
+@property (nonatomic, strong) NSLayoutConstraint *leftButtonWC;
+@property (nonatomic, strong) NSLayoutConstraint *leftButtonHC;
+@property (nonatomic, strong) NSLayoutConstraint *leftMarginWC;
+@property (nonatomic, strong) NSLayoutConstraint *bottomMarginWC;
+@property (nonatomic, strong) NSLayoutConstraint *rightButtonWC;
+@property (nonatomic, strong) NSLayoutConstraint *rightMarginWC;
+@property (nonatomic, strong) NSLayoutConstraint *editorContentViewHC;
+@property (nonatomic, strong) NSArray *charCountLabelVCs;
+
+@property (nonatomic, strong) UILabel *charCountLabel;
+
+@property (nonatomic, strong) Class textViewClass;
+
+@end
+
+@implementation SLKTextInputbar
+
+#pragma mark - Initialization
+
+- (instancetype)initWithTextViewClass:(Class)textViewClass
+{
+ if (self = [super init]) {
+ self.textViewClass = textViewClass;
+ [self slk_commonInit];
+ }
+ return self;
+}
+
+- (id)init
+{
+ if (self = [super init]) {
+ [self slk_commonInit];
+ }
+ return self;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)coder
+{
+ if (self = [super initWithCoder:coder]) {
+ [self slk_commonInit];
+ }
+ return self;
+}
+
+- (void)slk_commonInit
+{
+ self.charCountLabelNormalColor = [UIColor lightGrayColor];
+ self.charCountLabelWarningColor = [UIColor redColor];
+
+ self.autoHideRightButton = YES;
+ self.editorContentViewHeight = 38.0;
+ self.contentInset = UIEdgeInsetsMake(5.0, 8.0, 5.0, 8.0);
+
+ [self addSubview:self.editorContentView];
+ [self addSubview:self.leftButton];
+ [self addSubview:self.rightButton];
+ [self addSubview:self.textView];
+ [self addSubview:self.charCountLabel];
+
+ [self slk_setupViewConstraints];
+ [self slk_updateConstraintConstants];
+
+ self.counterStyle = SLKCounterStyleNone;
+ self.counterPosition = SLKCounterPositionTop;
+
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_didChangeTextViewText:) name:UITextViewTextDidChangeNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_didChangeTextViewContentSize:) name:SLKTextViewContentSizeDidChangeNotification object:nil];
+
+ [self.leftButton.imageView addObserver:self forKeyPath:NSStringFromSelector(@selector(image)) options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:NULL];
+}
+
+
+#pragma mark - UIView Overrides
+
+- (void)layoutIfNeeded
+{
+ if (self.constraints.count == 0) {
+ return;
+ }
+
+ [self slk_updateConstraintConstants];
+ [super layoutIfNeeded];
+}
+
+- (CGSize)intrinsicContentSize
+{
+ return CGSizeMake(UIViewNoIntrinsicMetric, 44.0);
+}
+
++ (BOOL)requiresConstraintBasedLayout
+{
+ return YES;
+}
+
+
+#pragma mark - Getters
+
+- (SLKTextView *)textView
+{
+ if (!_textView)
+ {
+ Class class = self.textViewClass ? : [SLKTextView class];
+
+ _textView = [[class alloc] init];
+ _textView.translatesAutoresizingMaskIntoConstraints = NO;
+ _textView.font = [UIFont systemFontOfSize:15.0];
+ _textView.maxNumberOfLines = [self slk_defaultNumberOfLines];
+
+ _textView.typingSuggestionEnabled = YES;
+ _textView.autocapitalizationType = UITextAutocapitalizationTypeSentences;
+ _textView.keyboardType = UIKeyboardTypeTwitter;
+ _textView.returnKeyType = UIReturnKeyDefault;
+ _textView.enablesReturnKeyAutomatically = YES;
+ _textView.scrollIndicatorInsets = UIEdgeInsetsMake(0.0, -1.0, 0.0, 1.0);
+ _textView.textContainerInset = UIEdgeInsetsMake(8.0, 4.0, 8.0, 0.0);
+ _textView.layer.cornerRadius = 5.0;
+ _textView.layer.borderWidth = 0.5;
+ _textView.layer.borderColor = [UIColor colorWithRed:200.0/255.0 green:200.0/255.0 blue:205.0/255.0 alpha:1.0].CGColor;
+
+ // Adds an aditional action to a private gesture to detect when the magnifying glass becomes visible
+ for (UIGestureRecognizer *gesture in _textView.gestureRecognizers) {
+ if ([gesture isKindOfClass:NSClassFromString(@"UIVariableDelayLoupeGesture")]) {
+ [gesture addTarget:self action:@selector(slk_willShowLoupe:)];
+ }
+ }
+ }
+ return _textView;
+}
+
+- (UIButton *)leftButton
+{
+ if (!_leftButton)
+ {
+ _leftButton = [UIButton buttonWithType:UIButtonTypeSystem];
+ _leftButton.translatesAutoresizingMaskIntoConstraints = NO;
+ _leftButton.titleLabel.font = [UIFont systemFontOfSize:15.0];
+ }
+ return _leftButton;
+}
+
+- (UIButton *)rightButton
+{
+ if (!_rightButton)
+ {
+ _rightButton = [UIButton buttonWithType:UIButtonTypeSystem];
+ _rightButton.translatesAutoresizingMaskIntoConstraints = NO;
+ _rightButton.titleLabel.font = [UIFont boldSystemFontOfSize:15.0];
+ _rightButton.enabled = NO;
+
+ [_rightButton setTitle:NSLocalizedString(@"Send", nil) forState:UIControlStateNormal];
+ }
+ return _rightButton;
+}
+
+- (UIView *)editorContentView
+{
+ if (!_editorContentView)
+ {
+ _editorContentView = [UIView new];
+ _editorContentView.translatesAutoresizingMaskIntoConstraints = NO;
+ _editorContentView.backgroundColor = self.backgroundColor;
+ _editorContentView.clipsToBounds = YES;
+ _editorContentView.hidden = YES;
+
+ _editorTitle = [UILabel new];
+ _editorTitle.translatesAutoresizingMaskIntoConstraints = NO;
+ _editorTitle.text = NSLocalizedString(@"Editing Message", nil);
+ _editorTitle.textAlignment = NSTextAlignmentCenter;
+ _editorTitle.backgroundColor = [UIColor clearColor];
+ _editorTitle.font = [UIFont boldSystemFontOfSize:15.0];
+ [_editorContentView addSubview:self.editorTitle];
+
+ _editortLeftButton = [UIButton buttonWithType:UIButtonTypeSystem];
+ _editortLeftButton.translatesAutoresizingMaskIntoConstraints = NO;
+ _editortLeftButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
+ _editortLeftButton.titleLabel.font = [UIFont systemFontOfSize:15.0];
+ [_editortLeftButton setTitle:NSLocalizedString(@"Cancel", nil) forState:UIControlStateNormal];
+ [_editorContentView addSubview:self.editortLeftButton];
+
+ _editortRightButton = [UIButton buttonWithType:UIButtonTypeSystem];
+ _editortRightButton.translatesAutoresizingMaskIntoConstraints = NO;
+ _editortRightButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentRight;
+ _editortRightButton.titleLabel.font = [UIFont boldSystemFontOfSize:15.0];
+ _editortRightButton.enabled = NO;
+ [_editortRightButton setTitle:NSLocalizedString(@"Save", nil) forState:UIControlStateNormal];
+ [_editorContentView addSubview:self.editortRightButton];
+
+ NSDictionary *views = @{@"label": self.editorTitle,
+ @"leftButton": self.editortLeftButton,
+ @"rightButton": self.editortRightButton,
+ };
+
+ NSDictionary *metrics = @{@"left" : @(self.contentInset.left),
+ @"right" : @(self.contentInset.right)
+ };
+
+ [_editorContentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(left)-[leftButton(60)]-(left)-[label(>=0)]-(right)-[rightButton(60)]-(<=right)-|" options:0 metrics:metrics views:views]];
+ [_editorContentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[leftButton]|" options:0 metrics:metrics views:views]];
+ [_editorContentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[rightButton]|" options:0 metrics:metrics views:views]];
+ [_editorContentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[label]|" options:0 metrics:metrics views:views]];
+ }
+ return _editorContentView;
+}
+
+- (UILabel *)charCountLabel
+{
+ if (!_charCountLabel)
+ {
+ _charCountLabel = [UILabel new];
+ _charCountLabel.translatesAutoresizingMaskIntoConstraints = NO;
+ _charCountLabel.backgroundColor = [UIColor clearColor];
+ _charCountLabel.textAlignment = NSTextAlignmentRight;
+ _charCountLabel.font = [UIFont systemFontOfSize:11.0];
+
+ _charCountLabel.hidden = NO;
+ }
+ return _charCountLabel;
+}
+
+- (BOOL)isViewVisible
+{
+ SEL selector = NSSelectorFromString(@"isViewVisible");
+
+ if ([self.controller respondsToSelector:selector]) {
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+ BOOL visible = (BOOL)[self.controller performSelector:selector];
+#pragma clang diagnostic pop
+
+ return visible;
+ }
+ return NO;
+}
+
+- (CGFloat)minimumInputbarHeight
+{
+ return self.intrinsicContentSize.height;
+}
+
+- (CGFloat)appropriateHeight
+{
+ CGFloat height = 0.0;
+ CGFloat minimumHeight = [self minimumInputbarHeight];
+
+ if (self.textView.numberOfLines == 1) {
+ height = minimumHeight;
+ }
+ else if (self.textView.numberOfLines < self.textView.maxNumberOfLines) {
+ height = [self slk_inputBarHeightForLines:self.textView.numberOfLines];
+ }
+ else {
+ height = [self slk_inputBarHeightForLines:self.textView.maxNumberOfLines];
+ }
+
+ if (height < minimumHeight) {
+ height = minimumHeight;
+ }
+
+ if (self.isEditing) {
+ height += self.editorContentViewHeight;
+ }
+
+ return roundf(height);
+}
+
+- (CGFloat)slk_deltaInputbarHeight
+{
+ return self.textView.intrinsicContentSize.height-self.textView.font.lineHeight;
+}
+
+- (CGFloat)slk_inputBarHeightForLines:(NSUInteger)numberOfLines
+{
+ CGFloat height = [self slk_deltaInputbarHeight];
+
+ height += roundf(self.textView.font.lineHeight*numberOfLines);
+ height += self.contentInset.top+self.contentInset.bottom;
+
+ return height;
+}
+
+- (BOOL)limitExceeded
+{
+ NSString *text = [self.textView.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
+
+ if (self.maxCharCount > 0 && text.length > self.maxCharCount) {
+ return YES;
+ }
+ return NO;
+}
+
+- (CGFloat)slk_appropriateRightButtonWidth
+{
+ NSString *title = [self.rightButton titleForState:UIControlStateNormal];
+ CGSize rigthButtonSize = [title sizeWithAttributes:@{NSFontAttributeName: self.rightButton.titleLabel.font}];
+
+ if (self.autoHideRightButton) {
+ if (self.textView.text.length == 0) {
+ return 0.0;
+ }
+ }
+ return rigthButtonSize.width+self.contentInset.right;
+}
+
+- (CGFloat)slk_appropriateRightButtonMargin
+{
+ if (self.autoHideRightButton) {
+ if (self.textView.text.length == 0) {
+ return 0.0;
+ }
+ }
+ return self.contentInset.right;
+}
+
+- (NSUInteger)slk_defaultNumberOfLines
+{
+ if (SLK_IS_IPAD) {
+ return 8;
+ }
+ if (SLK_IS_IPHONE4) {
+ return 4;
+ }
+ else {
+ return 6;
+ }
+}
+
+
+#pragma mark - Setters
+
+- (void)setBackgroundColor:(UIColor *)color
+{
+ self.barTintColor = color;
+ self.textView.inputAccessoryView.backgroundColor = color;
+ self.editorContentView.backgroundColor = color;
+}
+
+- (void)setAutoHideRightButton:(BOOL)hide
+{
+ if (self.autoHideRightButton == hide) {
+ return;
+ }
+
+ _autoHideRightButton = hide;
+
+ self.rightButtonWC.constant = [self slk_appropriateRightButtonWidth];
+ [self layoutIfNeeded];
+}
+
+- (void)setContentInset:(UIEdgeInsets)insets
+{
+ if (UIEdgeInsetsEqualToEdgeInsets(self.contentInset, insets)) {
+ return;
+ }
+
+ if (UIEdgeInsetsEqualToEdgeInsets(self.contentInset, UIEdgeInsetsZero)) {
+ _contentInset = insets;
+ return;
+ }
+
+ _contentInset = insets;
+
+ // Add new constraints
+ [self removeConstraints:self.constraints];
+ [self slk_setupViewConstraints];
+
+ // Add constant values and refresh layout
+ [self slk_updateConstraintConstants];
+ [super layoutIfNeeded];
+}
+
+- (void)setEditing:(BOOL)editing
+{
+ if (self.isEditing == editing) {
+ return;
+ }
+
+ _editing = editing;
+ _editorContentView.hidden = !editing;
+}
+
+- (void)setCounterPosition:(SLKCounterPosition)counterPosition
+{
+ if (self.counterPosition == counterPosition && self.charCountLabelVCs) {
+ return;
+ }
+
+ // Clears the previous constraints
+ if (_charCountLabelVCs.count > 0) {
+ [self removeConstraints:_charCountLabelVCs];
+ _charCountLabelVCs = nil;
+ }
+
+ _counterPosition = counterPosition;
+
+ NSDictionary *views = @{@"rightButton": self.rightButton,
+ @"charCountLabel": self.charCountLabel
+ };
+
+ NSDictionary *metrics = @{@"top" : @(self.contentInset.top),
+ @"bottom" : @(-self.contentInset.bottom/2.0)
+ };
+
+ // Constraints are different depending of the counter's position type
+ if (counterPosition == SLKCounterPositionBottom) {
+ _charCountLabelVCs = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[charCountLabel]-(bottom)-[rightButton]" options:0 metrics:metrics views:views];
+ }
+ else {
+ _charCountLabelVCs = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(<=top)-[charCountLabel]-(>=0)-|" options:0 metrics:metrics views:views];
+ }
+
+ [self addConstraints:self.charCountLabelVCs];
+}
+
+
+#pragma mark - Text Editing
+
+- (BOOL)canEditText:(NSString *)text
+{
+ if (self.isEditing && [self.textView.text isEqualToString:text]) {
+ return NO;
+ }
+
+ return YES;
+}
+
+- (void)beginTextEditing
+{
+ if (self.isEditing) {
+ return;
+ }
+
+ self.editing = YES;
+
+ [self slk_updateConstraintConstants];
+
+ if (!self.isFirstResponder) {
+ [self layoutIfNeeded];
+ }
+}
+
+- (void)endTextEdition
+{
+ if (!self.isEditing) {
+ return;
+ }
+
+ self.editing = NO;
+ [self slk_updateConstraintConstants];
+}
+
+
+#pragma mark - Character Counter
+
+- (void)slk_updateCounter
+{
+ NSString *text = [self.textView.text stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];
+ NSString *counter = nil;
+
+ if (self.counterStyle == SLKCounterStyleNone) {
+ counter = [NSString stringWithFormat:@"%lu", (unsigned long)text.length];
+ }
+ if (self.counterStyle == SLKCounterStyleSplit) {
+ counter = [NSString stringWithFormat:@"%lu/%lu", (unsigned long)text.length, (unsigned long)self.maxCharCount];
+ }
+ if (self.counterStyle == SLKCounterStyleCountdown) {
+ counter = [NSString stringWithFormat:@"%ld", (long)(text.length - self.maxCharCount)];
+ }
+ if (self.counterStyle == SLKCounterStyleCountdownReversed)
+ {
+ counter = [NSString stringWithFormat:@"%ld", (long)(self.maxCharCount - text.length)];
+ }
+
+ self.charCountLabel.text = counter;
+ self.charCountLabel.textColor = [self limitExceeded] ? self.charCountLabelWarningColor : self.charCountLabelNormalColor;
+}
+
+
+#pragma mark - Magnifying Glass handling
+
+- (void)slk_willShowLoupe:(UIGestureRecognizer *)gesture
+{
+ if (gesture.state == UIGestureRecognizerStateChanged) {
+ self.textView.loupeVisible = YES;
+ }
+ else {
+ self.textView.loupeVisible = NO;
+ }
+
+ // We still need to notify a selection change in the textview after the magnifying class is dismissed
+ if (gesture.state == UIGestureRecognizerStateEnded) {
+ if (self.textView.delegate && [self.textView.delegate respondsToSelector:@selector(textViewDidChangeSelection:)]) {
+ [self.textView.delegate textViewDidChangeSelection:self.textView];
+ }
+
+ [[NSNotificationCenter defaultCenter] postNotificationName:SLKTextViewSelectedRangeDidChangeNotification object:self.textView userInfo:nil];
+ }
+}
+
+
+#pragma mark - Notification Events
+
+- (void)slk_didChangeTextViewText:(NSNotification *)notification
+{
+ SLKTextView *textView = (SLKTextView *)notification.object;
+
+ // Skips this it's not the expected textView.
+ if (![textView isEqual:self.textView]) {
+ return;
+ }
+
+ // Updates the char counter label
+ if (self.maxCharCount > 0) {
+ [self slk_updateCounter];
+ }
+
+ if (self.autoHideRightButton && !self.isEditing)
+ {
+ CGFloat rightButtonNewWidth = [self slk_appropriateRightButtonWidth];
+
+ if (self.rightButtonWC.constant == rightButtonNewWidth) {
+ return;
+ }
+
+ self.rightButtonWC.constant = rightButtonNewWidth;
+ self.rightMarginWC.constant = [self slk_appropriateRightButtonMargin];
+
+ if (rightButtonNewWidth > 0) {
+ [self.rightButton sizeToFit];
+ }
+
+ BOOL bounces = self.controller.bounces && [self.textView isFirstResponder];
+
+ if ([self isViewVisible]) {
+ [self slk_animateLayoutIfNeededWithBounce:bounces
+ options:UIViewAnimationOptionCurveEaseInOut|UIViewAnimationOptionBeginFromCurrentState
+ animations:NULL];
+ }
+ else {
+ [self layoutIfNeeded];
+ }
+ }
+}
+
+- (void)slk_didChangeTextViewContentSize:(NSNotification *)notification
+{
+ if (self.maxCharCount > 0) {
+ BOOL shouldHide = (self.textView.numberOfLines == 1) || self.editing;
+ self.charCountLabel.hidden = shouldHide;
+ }
+}
+
+
+#pragma mark - View Auto-Layout
+
+- (void)slk_setupViewConstraints
+{
+ UIImage *leftButtonImg = [self.leftButton imageForState:UIControlStateNormal];
+
+ [self.rightButton sizeToFit];
+
+ CGFloat leftVerMargin = (self.intrinsicContentSize.height - leftButtonImg.size.height) / 2.0;
+ CGFloat rightVerMargin = (self.intrinsicContentSize.height - CGRectGetHeight(self.rightButton.frame)) / 2.0;
+
+ NSDictionary *views = @{@"textView": self.textView,
+ @"leftButton": self.leftButton,
+ @"rightButton": self.rightButton,
+ @"contentView": self.editorContentView,
+ @"charCountLabel": self.charCountLabel
+ };
+
+ NSDictionary *metrics = @{@"top" : @(self.contentInset.top),
+ @"bottom" : @(self.contentInset.bottom),
+ @"left" : @(self.contentInset.left),
+ @"right" : @(self.contentInset.right),
+ @"leftVerMargin" : @(leftVerMargin),
+ @"rightVerMargin" : @(rightVerMargin),
+ @"minTextViewHeight" : @(self.textView.intrinsicContentSize.height),
+ };
+
+ [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(left)-[leftButton(0)]-(<=left)-[textView]-(right)-[rightButton(0)]-(right)-|" options:0 metrics:metrics views:views]];
+ [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(>=0)-[leftButton(0)]-(0@750)-|" options:0 metrics:metrics views:views]];
+ [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(>=rightVerMargin)-[rightButton]-(<=rightVerMargin)-|" options:0 metrics:metrics views:views]];
+ [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(left@250)-[charCountLabel(<=50@1000)]-(right@750)-|" options:0 metrics:metrics views:views]];
+
+ [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[contentView(0)]-(<=top)-[textView(minTextViewHeight@250)]-(bottom)-|" options:0 metrics:metrics views:views]];
+ [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[contentView]|" options:0 metrics:metrics views:views]];
+
+ self.editorContentViewHC = [self slk_constraintForAttribute:NSLayoutAttributeHeight firstItem:self.editorContentView secondItem:nil];
+
+ self.leftButtonWC = [self slk_constraintForAttribute:NSLayoutAttributeWidth firstItem:self.leftButton secondItem:nil];
+ self.leftButtonHC = [self slk_constraintForAttribute:NSLayoutAttributeHeight firstItem:self.leftButton secondItem:nil];
+
+ self.leftMarginWC = [self slk_constraintsForAttribute:NSLayoutAttributeLeading][0];
+ self.bottomMarginWC = [self slk_constraintForAttribute:NSLayoutAttributeBottom firstItem:self secondItem:self.leftButton];
+
+ self.rightButtonWC = [self slk_constraintForAttribute:NSLayoutAttributeWidth firstItem:self.rightButton secondItem:nil];
+ self.rightMarginWC = [self slk_constraintsForAttribute:NSLayoutAttributeTrailing][0];
+}
+
+- (void)slk_updateConstraintConstants
+{
+ CGFloat zero = 0.0;
+
+ if (self.isEditing)
+ {
+ self.editorContentViewHC.constant = self.editorContentViewHeight;
+ self.leftButtonWC.constant = zero;
+ self.leftButtonHC.constant = zero;
+ self.leftMarginWC.constant = zero;
+ self.bottomMarginWC.constant = zero;
+ self.rightButtonWC.constant = zero;
+ self.rightMarginWC.constant = zero;
+ }
+ else {
+ self.editorContentViewHC.constant = zero;
+
+ CGSize leftButtonSize = [self.leftButton imageForState:self.leftButton.state].size;
+
+ if (leftButtonSize.width > 0) {
+ self.leftButtonHC.constant = roundf(leftButtonSize.height);
+ self.bottomMarginWC.constant = roundf((self.intrinsicContentSize.height - leftButtonSize.height) / 2.0);
+ }
+
+ self.leftButtonWC.constant = roundf(leftButtonSize.width);
+ self.leftMarginWC.constant = (leftButtonSize.width > 0) ? self.contentInset.left : zero;
+
+ self.rightButtonWC.constant = [self slk_appropriateRightButtonWidth];
+ self.rightMarginWC.constant = [self slk_appropriateRightButtonMargin];
+ }
+}
+
+#pragma mark - Observers
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
+{
+ if ([object isEqual:self.leftButton.imageView] && [keyPath isEqualToString:NSStringFromSelector(@selector(image))]) {
+ UIImage *newImage = change[NSKeyValueChangeNewKey];
+ UIImage *oldImage = change[NSKeyValueChangeOldKey];
+
+ if ([newImage isEqual:oldImage]) {
+ return;
+ }
+
+ [self slk_updateConstraintConstants];
+ }
+ else {
+ [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
+ }
+}
+
+
+#pragma mark - Lifeterm
+
+- (void)dealloc
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:UITextViewTextDidChangeNotification object:nil];
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:SLKTextViewContentSizeDidChangeNotification object:nil];
+
+ [_leftButton.imageView removeObserver:self forKeyPath:NSStringFromSelector(@selector(image))];
+
+ _leftButton = nil;
+ _rightButton = nil;
+
+ _textView.delegate = nil;
+ _textView = nil;
+
+ _editorContentView = nil;
+ _editorTitle = nil;
+ _editortLeftButton = nil;
+ _editortRightButton = nil;
+
+ _leftButtonWC = nil;
+ _leftButtonHC = nil;
+ _leftMarginWC = nil;
+ _bottomMarginWC = nil;
+ _rightButtonWC = nil;
+ _rightMarginWC = nil;
+ _editorContentViewHC = nil;
+}
+
+@end
diff --git a/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Classes/SLKTextView.h b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Classes/SLKTextView.h
new file mode 100644
index 000000000..51532af17
--- /dev/null
+++ b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Classes/SLKTextView.h
@@ -0,0 +1,90 @@
+//
+// Copyright 2014 Slack Technologies, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import
+
+UIKIT_EXTERN NSString * const SLKTextViewTextWillChangeNotification;
+UIKIT_EXTERN NSString * const SLKTextViewContentSizeDidChangeNotification;
+UIKIT_EXTERN NSString * const SLKTextViewSelectedRangeDidChangeNotification;
+UIKIT_EXTERN NSString * const SLKTextViewDidPasteItemNotification;
+UIKIT_EXTERN NSString * const SLKTextViewDidShakeNotification;
+
+UIKIT_EXTERN NSString * const SLKTextViewPastedItemContentType;
+UIKIT_EXTERN NSString * const SLKTextViewPastedItemMediaType;
+UIKIT_EXTERN NSString * const SLKTextViewPastedItemData;
+
+typedef NS_OPTIONS(NSUInteger, SLKPastableMediaType) {
+ SLKPastableMediaTypeNone = 0,
+ SLKPastableMediaTypePNG = 1 << 0,
+ SLKPastableMediaTypeJPEG = 1 << 1,
+ SLKPastableMediaTypeTIFF = 1 << 2,
+ SLKPastableMediaTypeGIF = 1 << 3,
+ SLKPastableMediaTypeMOV = 1 << 4,
+ SLKPastableMediaTypePassbook = 1 << 5,
+ SLKPastableMediaTypeImages = SLKPastableMediaTypePNG|SLKPastableMediaTypeJPEG|SLKPastableMediaTypeTIFF|SLKPastableMediaTypeGIF,
+ SLKPastableMediaTypeVideos = SLKPastableMediaTypeMOV,
+ SLKPastableMediaTypeAll = SLKPastableMediaTypeImages|SLKPastableMediaTypeMOV
+};
+
+/** @name A custom text input view. */
+@interface SLKTextView : UITextView
+
+/** The placeholder text string. Default is nil. */
+@property (nonatomic, copy) NSString *placeholder;
+
+/** The placeholder color. Default is lightGrayColor. */
+@property (nonatomic, copy) UIColor *placeholderColor;
+
+/** The maximum number of lines before enabling scrolling. Default is 0 wich means limitless. */
+@property (nonatomic, readwrite) NSUInteger maxNumberOfLines;
+
+/** The current displayed number of lines. */
+@property (nonatomic, readonly) NSUInteger numberOfLines;
+
+/** The supported media types allowed to be pasted in the text view, such as images or videos. Default is None. */
+@property (nonatomic) SLKPastableMediaType pastableMediaTypes;
+
+/** YES if the text view is and can still expand it self, depending if the maximum number of lines are reached. */
+@property (nonatomic, readonly) BOOL isExpanding;
+
+/** YES if quickly refreshed the textview without the intension to dismiss the keyboard. @view -disableQuicktypeBar: for more details. */
+@property (nonatomic, readwrite) BOOL didNotResignFirstResponder;
+
+/** YES if the magnifying glass is visible. */
+@property (nonatomic, getter=isLoupeVisible) BOOL loupeVisible;
+
+/** YES if autocorrection and spell checking are enabled. On iOS8, this property also controls the predictive QuickType bar from being visible. Default is YES. */
+@property (nonatomic, getter=isTypingSuggestionEnabled) BOOL typingSuggestionEnabled;
+
+/** YES if the text view supports undoing, either using UIMenuController, or with ctrl+z when using an external keyboard. Default is YES. */
+@property (nonatomic, readwrite) BOOL undoManagerEnabled;
+
+/**
+ Some text view properties don't update when it's already firstResponder (auto-correction, spelling-check, etc.)
+ To be able to update the text view while still being first responder, requieres to switch quickly from -resignFirstResponder to -becomeFirstResponder.
+ When doing so, the flag 'didNotResignFirstResponder' is momentarly set to YES before it goes back to -isFirstResponder, to be able to prevent some tasks to be excuted because of UIKeyboard notifications.
+
+ You can also use this method to confirm an auto-correction programatically, before the text view resigns first responder.
+ */
+- (void)refreshFirstResponder;
+- (void)refreshInputViews;
+
+/**
+ Notifies the text view that the user pressed any arrow key. This is used to move the cursor up and down while having multiple lines.
+ */
+- (void)didPressAnyArrowKey:(id)sender;
+
+@end
diff --git a/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Classes/SLKTextView.m b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Classes/SLKTextView.m
new file mode 100644
index 000000000..e4898fdd4
--- /dev/null
+++ b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Classes/SLKTextView.m
@@ -0,0 +1,796 @@
+//
+// Copyright 2014 Slack Technologies, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import "SLKTextView.h"
+#import "SLKTextView+SLKAdditions.h"
+#import "SLKUIConstants.h"
+
+NSString * const SLKTextViewTextWillChangeNotification = @"SLKTextViewTextWillChangeNotification";
+NSString * const SLKTextViewContentSizeDidChangeNotification = @"SLKTextViewContentSizeDidChangeNotification";
+NSString * const SLKTextViewSelectedRangeDidChangeNotification = @"SLKTextViewSelectedRangeDidChangeNotification";
+NSString * const SLKTextViewDidPasteItemNotification = @"SLKTextViewDidPasteItemNotification";
+NSString * const SLKTextViewDidShakeNotification = @"SLKTextViewDidShakeNotification";
+
+NSString * const SLKTextViewPastedItemContentType = @"SLKTextViewPastedItemContentType";
+NSString * const SLKTextViewPastedItemMediaType = @"SLKTextViewPastedItemMediaType";
+NSString * const SLKTextViewPastedItemData = @"SLKTextViewPastedItemData";
+
+@interface SLKTextView ()
+
+// The label used as placeholder
+@property (nonatomic, strong) UILabel *placeholderLabel;
+
+// The keyboard commands available for external keyboards
+@property (nonatomic, strong) NSArray *keyboardCommands;
+
+// Used for moving the care up/down
+@property (nonatomic) UITextLayoutDirection verticalMoveDirection;
+@property (nonatomic) CGRect verticalMoveStartCaretRect;
+@property (nonatomic) CGRect verticalMoveLastCaretRect;
+
+// Used for detecting if the scroll indicator was previously flashed
+@property (nonatomic) BOOL didFlashScrollIndicators;
+
+@end
+
+@implementation SLKTextView
+
+#pragma mark - Initialization
+
+- (instancetype)initWithFrame:(CGRect)frame textContainer:(NSTextContainer *)textContainer
+{
+ if (self = [super initWithFrame:frame textContainer:textContainer]) {
+ [self slk_commonInit];
+ }
+ return self;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)coder
+{
+ if (self = [super initWithCoder:coder]) {
+ [self slk_commonInit];
+ }
+ return self;
+}
+
+- (void)slk_commonInit
+{
+ self.pastableMediaTypes = SLKPastableMediaTypeNone;
+ self.undoManagerEnabled = YES;
+
+ self.font = [UIFont systemFontOfSize:14.0];
+ self.editable = YES;
+ self.selectable = YES;
+ self.scrollEnabled = YES;
+ self.scrollsToTop = NO;
+ self.directionalLockEnabled = YES;
+ self.dataDetectorTypes = UIDataDetectorTypeNone;
+
+ // UITextView notifications
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_didBeginEditing:) name:UITextViewTextDidBeginEditingNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_didChangeText:) name:UITextViewTextDidChangeNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_didEndEditing:) name:UITextViewTextDidEndEditingNotification object:nil];
+
+ [self addObserver:self forKeyPath:NSStringFromSelector(@selector(contentSize)) options:NSKeyValueObservingOptionNew context:NULL];
+}
+
+
+#pragma mark - UIView Overrides
+
+- (CGSize)intrinsicContentSize
+{
+ return CGSizeMake(UIViewNoIntrinsicMetric, 34.0);
+}
+
++ (BOOL)requiresConstraintBasedLayout
+{
+ return YES;
+}
+
+- (void)layoutIfNeeded
+{
+ [super layoutIfNeeded];
+}
+
+- (void)layoutSubviews
+{
+ [super layoutSubviews];
+
+ self.placeholderLabel.hidden = [self slk_shouldHidePlaceholder];
+
+ if (!self.placeholderLabel.hidden) {
+
+ [UIView performWithoutAnimation:^{
+ self.placeholderLabel.frame = [self slk_placeholderRectThatFits:self.bounds];
+ [self sendSubviewToBack:self.placeholderLabel];
+ }];
+ }
+}
+
+
+#pragma mark - Getters
+
+- (UILabel *)placeholderLabel
+{
+ if (!_placeholderLabel)
+ {
+ _placeholderLabel = [UILabel new];
+ _placeholderLabel.clipsToBounds = NO;
+ _placeholderLabel.autoresizesSubviews = NO;
+ _placeholderLabel.numberOfLines = 1;
+ _placeholderLabel.font = self.font;
+ _placeholderLabel.backgroundColor = [UIColor clearColor];
+ _placeholderLabel.textColor = [UIColor lightGrayColor];
+ _placeholderLabel.hidden = YES;
+
+ [self addSubview:_placeholderLabel];
+ }
+ return _placeholderLabel;
+}
+
+- (NSString *)placeholder
+{
+ return self.placeholderLabel.text;
+}
+
+- (UIColor *)placeholderColor
+{
+ return self.placeholderLabel.textColor;
+}
+
+- (NSUInteger)numberOfLines
+{
+ return fabs(self.contentSize.height/self.font.lineHeight);
+}
+
+// Returns a different number of lines when landscape and only on iPhone
+- (NSUInteger)maxNumberOfLines
+{
+ if (SLK_IS_IPHONE && SLK_IS_LANDSCAPE) {
+ return 2.0;
+ }
+ return _maxNumberOfLines;
+}
+
+- (BOOL)isTypingSuggestionEnabled
+{
+ return (self.autocorrectionType == UITextAutocorrectionTypeNo) ? NO : YES;
+}
+
+// Returns only a supported pasted item
+- (id)slk_pastedItem
+{
+ NSString *contentType = [self slk_pasteboardContentType];
+ NSData *data = [[UIPasteboard generalPasteboard] dataForPasteboardType:contentType];
+
+ if (data && [data isKindOfClass:[NSData class]])
+ {
+ SLKPastableMediaType mediaType = SLKPastableMediaTypeFromNSString(contentType);
+
+ NSDictionary *userInfo = @{SLKTextViewPastedItemContentType: contentType,
+ SLKTextViewPastedItemMediaType: @(mediaType),
+ SLKTextViewPastedItemData: data};
+ return userInfo;
+ }
+ if ([[UIPasteboard generalPasteboard] URL]) {
+ return [[[UIPasteboard generalPasteboard] URL] absoluteString];
+ }
+ if ([[UIPasteboard generalPasteboard] string]) {
+ return [[UIPasteboard generalPasteboard] string];
+ }
+
+ return nil;
+}
+
+// Checks if any supported media found in the general pasteboard
+- (BOOL)slk_isPasteboardItemSupported
+{
+ if ([self slk_pasteboardContentType].length > 0) {
+ return YES;
+ }
+ return NO;
+}
+
+- (NSString *)slk_pasteboardContentType
+{
+ NSArray *pasteboardTypes = [[UIPasteboard generalPasteboard] pasteboardTypes];
+ NSMutableArray *subpredicates = [NSMutableArray new];
+
+ for (NSString *type in [self slk_supportedMediaTypes]) {
+ [subpredicates addObject:[NSPredicate predicateWithFormat:@"SELF == %@", type]];
+ }
+
+ return [[pasteboardTypes filteredArrayUsingPredicate:[NSCompoundPredicate orPredicateWithSubpredicates:subpredicates]] firstObject];
+}
+
+- (NSArray *)slk_supportedMediaTypes
+{
+ if (self.pastableMediaTypes == SLKPastableMediaTypeNone) {
+ return nil;
+ }
+
+ NSMutableArray *types = [NSMutableArray new];
+
+ if (self.pastableMediaTypes & SLKPastableMediaTypePNG) {
+ [types addObject:NSStringFromSLKPastableMediaType(SLKPastableMediaTypePNG)];
+ }
+ if (self.pastableMediaTypes & SLKPastableMediaTypeJPEG) {
+ [types addObject:NSStringFromSLKPastableMediaType(SLKPastableMediaTypeJPEG)];
+ }
+ if (self.pastableMediaTypes & SLKPastableMediaTypeTIFF) {
+ [types addObject:NSStringFromSLKPastableMediaType(SLKPastableMediaTypeTIFF)];
+ }
+ if (self.pastableMediaTypes & SLKPastableMediaTypeGIF) {
+ [types addObject:NSStringFromSLKPastableMediaType(SLKPastableMediaTypeGIF)];
+ }
+ if (self.pastableMediaTypes & SLKPastableMediaTypeMOV) {
+ [types addObject:NSStringFromSLKPastableMediaType(SLKPastableMediaTypeMOV)];
+ }
+ if (self.pastableMediaTypes & SLKPastableMediaTypePassbook) {
+ [types addObject:NSStringFromSLKPastableMediaType(SLKPastableMediaTypePassbook)];
+ }
+
+ if (self.pastableMediaTypes & SLKPastableMediaTypeImages) {
+ [types addObject:NSStringFromSLKPastableMediaType(SLKPastableMediaTypeImages)];
+ }
+
+
+ return types;
+}
+
+NSString *NSStringFromSLKPastableMediaType(SLKPastableMediaType type)
+{
+ if (type == SLKPastableMediaTypePNG) {
+ return @"public.png";
+ }
+ if (type == SLKPastableMediaTypeJPEG) {
+ return @"public.jpeg";
+ }
+ if (type == SLKPastableMediaTypeTIFF) {
+ return @"public.tiff";
+ }
+ if (type == SLKPastableMediaTypeGIF) {
+ return @"com.compuserve.gif";
+ }
+ if (type == SLKPastableMediaTypeMOV) {
+ return @"com.apple.quicktime";
+ }
+ if (type == SLKPastableMediaTypePassbook) {
+ return @"com.apple.pkpass";
+ }
+ if (type == SLKPastableMediaTypeImages) {
+ return @"com.apple.uikit.image";
+ }
+
+ return nil;
+}
+
+SLKPastableMediaType SLKPastableMediaTypeFromNSString(NSString *string)
+{
+ if ([string isEqualToString:NSStringFromSLKPastableMediaType(SLKPastableMediaTypePNG)]) {
+ return SLKPastableMediaTypePNG;
+ }
+ if ([string isEqualToString:NSStringFromSLKPastableMediaType(SLKPastableMediaTypeJPEG)]) {
+ return SLKPastableMediaTypeJPEG;
+ }
+ if ([string isEqualToString:NSStringFromSLKPastableMediaType(SLKPastableMediaTypeTIFF)]) {
+ return SLKPastableMediaTypeTIFF;
+ }
+ if ([string isEqualToString:NSStringFromSLKPastableMediaType(SLKPastableMediaTypeGIF)]) {
+ return SLKPastableMediaTypeGIF;
+ }
+ if ([string isEqualToString:NSStringFromSLKPastableMediaType(SLKPastableMediaTypeMOV)]) {
+ return SLKPastableMediaTypeMOV;
+ }
+ if ([string isEqualToString:NSStringFromSLKPastableMediaType(SLKPastableMediaTypePassbook)]) {
+ return SLKPastableMediaTypePassbook;
+ }
+ if ([string isEqualToString:NSStringFromSLKPastableMediaType(SLKPastableMediaTypeImages)]) {
+ return SLKPastableMediaTypeImages;
+ }
+ return SLKPastableMediaTypeNone;
+}
+
+- (BOOL)isExpanding
+{
+ if (self.numberOfLines >= self.maxNumberOfLines) {
+ return YES;
+ }
+ return NO;
+}
+
+- (BOOL)slk_shouldHidePlaceholder
+{
+ if (self.placeholder.length == 0 || self.text.length > 0) {
+ return YES;
+ }
+ return NO;
+}
+
+- (CGRect)slk_placeholderRectThatFits:(CGRect)bounds
+{
+ CGRect rect = CGRectZero;
+ rect.size = [self.placeholderLabel sizeThatFits:bounds.size];
+ rect.origin = UIEdgeInsetsInsetRect(bounds, self.textContainerInset).origin;
+
+ CGFloat padding = self.textContainer.lineFragmentPadding;
+ rect.origin.x += padding;
+
+ return rect;
+}
+
+
+#pragma mark - Setters
+
+- (void)setPlaceholder:(NSString *)placeholder
+{
+ self.placeholderLabel.text = placeholder;
+
+ [self setNeedsLayout];
+}
+
+- (void)setPlaceholderColor:(UIColor *)color
+{
+ self.placeholderLabel.textColor = color;
+}
+
+- (void)setUndoManagerEnabled:(BOOL)enabled
+{
+ if (self.undoManagerEnabled == enabled) {
+ return;
+ }
+
+ self.undoManager.levelsOfUndo = 10;
+ [self.undoManager removeAllActions];
+ [self.undoManager setActionIsDiscardable:YES];
+
+ _undoManagerEnabled = enabled;
+}
+
+- (void)setTypingSuggestionEnabled:(BOOL)enabled
+{
+ if (self.isTypingSuggestionEnabled == enabled) {
+ return;
+ }
+
+ self.autocorrectionType = enabled ? UITextAutocorrectionTypeDefault : UITextAutocorrectionTypeNo;
+ self.spellCheckingType = enabled ? UITextSpellCheckingTypeDefault : UITextSpellCheckingTypeNo;
+
+ [self refreshFirstResponder];
+}
+
+
+#pragma mark - UITextView Overrides
+
+- (void)setSelectedRange:(NSRange)selectedRange
+{
+ [super setSelectedRange:selectedRange];
+}
+
+- (void)setSelectedTextRange:(UITextRange *)selectedTextRange
+{
+ [super setSelectedTextRange:selectedTextRange];
+
+ [[NSNotificationCenter defaultCenter] postNotificationName:SLKTextViewSelectedRangeDidChangeNotification object:self userInfo:nil];
+}
+
+- (void)setText:(NSString *)text
+{
+ // Registers for undo management
+ [self slk_prepareForUndo:@"Text Set"];
+
+ [super setText:text];
+
+ [[NSNotificationCenter defaultCenter] postNotificationName:UITextViewTextDidChangeNotification object:self];
+}
+
+- (void)setAttributedText:(NSAttributedString *)attributedText
+{
+ // Registers for undo management
+ [self slk_prepareForUndo:@"Attributed Text Set"];
+
+ [super setAttributedText:attributedText];
+}
+
+- (void)setFont:(UIFont *)font
+{
+ [super setFont:font];
+
+ // Updates the placeholder font too
+ self.placeholderLabel.font = self.font;
+}
+
+- (void)setTextAlignment:(NSTextAlignment)textAlignment
+{
+ [super setTextAlignment:textAlignment];
+
+ // Updates the placeholder text alignment too
+ self.placeholderLabel.textAlignment = textAlignment;
+}
+
+
+#pragma mark - UITextInputTraits Overrides
+
+- (void)insertText:(NSString *)text
+{
+ [super insertText:text];
+}
+
+
+#pragma mark - UIResponder Overrides
+
+- (BOOL)canBecomeFirstResponder
+{
+ // Adds undo/redo items to the Menu Controller
+ if (self.undoManagerEnabled) {
+ UIMenuItem *undo = [[UIMenuItem alloc] initWithTitle:NSLocalizedString(@"Undo", nil) action:@selector(slk_undo:)];
+ UIMenuItem *redo = [[UIMenuItem alloc] initWithTitle:NSLocalizedString(@"Redo", nil) action:@selector(slk_redo:)];
+ [[UIMenuController sharedMenuController] setMenuItems:@[undo,redo]];
+ }
+
+ return [super canBecomeFirstResponder];
+}
+
+- (BOOL)becomeFirstResponder
+{
+ return [super becomeFirstResponder];
+}
+
+- (BOOL)canResignFirstResponder
+{
+ // Removes undo/redo items
+ if (self.undoManagerEnabled) {
+ [[UIMenuController sharedMenuController] setMenuItems:@[]];
+ [self.undoManager removeAllActions];
+ }
+
+ return [super canResignFirstResponder];
+}
+
+- (BOOL)resignFirstResponder
+{
+ return [super resignFirstResponder];
+}
+
+- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
+{
+ if (action == @selector(delete:)) {
+ return NO;
+ }
+
+ if ((action == @selector(copy:) || action == @selector(cut:))
+ && self.selectedRange.length > 0) {
+ return YES;
+ }
+
+ if (action == @selector(paste:) && [self slk_isPasteboardItemSupported]) {
+ return YES;
+ }
+
+ if (self.undoManagerEnabled) {
+ if (action == @selector(slk_undo:)) {
+ if (self.undoManager.undoActionIsDiscardable) {
+ return NO;
+ }
+ return [self.undoManager canUndo];
+ }
+ if (action == @selector(slk_redo:)) {
+ if (self.undoManager.redoActionIsDiscardable) {
+ return NO;
+ }
+ return [self.undoManager canRedo];
+ }
+ }
+
+ return [super canPerformAction:action withSender:sender];
+}
+
+- (void)paste:(id)sender
+{
+ id pastedItem = [self slk_pastedItem];
+
+ if ([pastedItem isKindOfClass:[NSDictionary class]]) {
+ [[NSNotificationCenter defaultCenter] postNotificationName:SLKTextViewDidPasteItemNotification object:nil userInfo:pastedItem];
+ }
+ else if ([pastedItem isKindOfClass:[NSString class]]) {
+ // Respect the delegate yo!
+ if (self.delegate && [self.delegate respondsToSelector:@selector(textView:shouldChangeTextInRange:replacementText:)]) {
+ if (![self.delegate textView:self shouldChangeTextInRange:self.selectedRange replacementText:pastedItem]) {
+ return;
+ }
+ }
+
+ // Inserting the text fixes a UITextView bug whitch automatically scrolls to the bottom
+ // and beyond scroll content size sometimes when the text is too long
+ [self slk_insertTextAtCaretRange:pastedItem];
+ }
+}
+
+
+#pragma mark - Custom Actions
+
+- (void)slk_flashScrollIndicatorsIfNeeded
+{
+ if (self.numberOfLines == self.maxNumberOfLines+1) {
+ if (!_didFlashScrollIndicators) {
+ _didFlashScrollIndicators = YES;
+ [super flashScrollIndicators];
+ }
+ }
+ else if (_didFlashScrollIndicators) {
+ _didFlashScrollIndicators = NO;
+ }
+}
+
+- (void)refreshFirstResponder
+{
+ if (!self.isFirstResponder) {
+ return;
+ }
+
+ _didNotResignFirstResponder = YES;
+ [self resignFirstResponder];
+
+ _didNotResignFirstResponder = NO;
+ [self becomeFirstResponder];
+}
+
+- (void)refreshInputViews
+{
+ _didNotResignFirstResponder = YES;
+
+ [super reloadInputViews];
+
+ _didNotResignFirstResponder = NO;
+}
+
+- (void)slk_undo:(id)sender
+{
+ [self.undoManager undo];
+}
+
+- (void)slk_redo:(id)sender
+{
+ [self.undoManager redo];
+}
+
+
+#pragma mark - Notification Events
+
+- (void)slk_didBeginEditing:(NSNotification *)notification
+{
+ if (![notification.object isEqual:self]) {
+ return;
+ }
+
+ // Do something
+}
+
+- (void)slk_didChangeText:(NSNotification *)notification
+{
+ if (![notification.object isEqual:self]) {
+ return;
+ }
+
+ if (self.placeholderLabel.hidden != [self slk_shouldHidePlaceholder]) {
+ [self setNeedsLayout];
+ }
+
+ [self slk_flashScrollIndicatorsIfNeeded];
+}
+
+- (void)slk_didEndEditing:(NSNotification *)notification
+{
+ if (![notification.object isEqual:self]) {
+ return;
+ }
+
+ // Do something
+}
+
+
+#pragma mark - KVO Listener
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
+{
+ if ([object isEqual:self] && [keyPath isEqualToString:NSStringFromSelector(@selector(contentSize))]) {
+ [[NSNotificationCenter defaultCenter] postNotificationName:SLKTextViewContentSizeDidChangeNotification object:self userInfo:nil];
+ }
+ else {
+ [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
+ }
+}
+
+
+#pragma mark - Motion Events
+
+- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
+{
+ if (event.type == UIEventTypeMotion && event.subtype == UIEventSubtypeMotionShake) {
+ [[NSNotificationCenter defaultCenter] postNotificationName:SLKTextViewDidShakeNotification object:self];
+ }
+}
+
+
+#pragma mark - External Keyboard Support
+
+- (NSArray *)keyCommands
+{
+ if (_keyboardCommands) {
+ return _keyboardCommands;
+ }
+
+ _keyboardCommands = @[
+ // Return
+ [UIKeyCommand keyCommandWithInput:@"\r" modifierFlags:UIKeyModifierShift action:@selector(slk_didPressLineBreakKeys:)],
+ [UIKeyCommand keyCommandWithInput:@"\r" modifierFlags:UIKeyModifierAlternate action:@selector(slk_didPressLineBreakKeys:)],
+ [UIKeyCommand keyCommandWithInput:@"\r" modifierFlags:UIKeyModifierControl action:@selector(slk_didPressLineBreakKeys:)],
+
+ // Undo/Redo
+ [UIKeyCommand keyCommandWithInput:@"z" modifierFlags:UIKeyModifierCommand action:@selector(slk_didPressCommandZKeys:)],
+ [UIKeyCommand keyCommandWithInput:@"z" modifierFlags:UIKeyModifierShift|UIKeyModifierCommand action:@selector(slk_didPressCommandZKeys:)],
+ ];
+
+ return _keyboardCommands;
+}
+
+
+#pragma mark Line Break
+
+- (void)slk_didPressLineBreakKeys:(id)sender
+{
+ [self slk_insertNewLineBreak];
+}
+
+
+#pragma mark Undo/Redo Text
+
+- (void)slk_didPressCommandZKeys:(id)sender
+{
+ if (!self.undoManagerEnabled) {
+ return;
+ }
+
+ UIKeyCommand *keyCommand = (UIKeyCommand *)sender;
+
+ if ((keyCommand.modifierFlags & UIKeyModifierShift) > 0) {
+
+ if ([self.undoManager canRedo]) {
+ [self.undoManager redo];
+ }
+ }
+ else {
+ if ([self.undoManager canUndo]) {
+ [self.undoManager undo];
+ }
+ }
+}
+
+#pragma mark Up/Down Cursor Movement
+
+- (void)didPressAnyArrowKey:(id)sender
+{
+ if (self.text.length == 0 || self.numberOfLines < 2) {
+ return;
+ }
+
+ UIKeyCommand *keyCommand = (UIKeyCommand *)sender;
+
+ if ([keyCommand.input isEqualToString:UIKeyInputUpArrow]) {
+ [self slk_moveCursorTodirection:UITextLayoutDirectionUp];
+ }
+ else if ([keyCommand.input isEqualToString:UIKeyInputDownArrow]) {
+ [self slk_moveCursorTodirection:UITextLayoutDirectionDown];
+ }
+}
+
+- (void)slk_moveCursorTodirection:(UITextLayoutDirection)direction
+{
+ UITextPosition *start = (direction == UITextLayoutDirectionUp) ? self.selectedTextRange.start : self.selectedTextRange.end;
+
+ if ([self slk_isNewVerticalMovementForPosition:start inDirection:direction]) {
+ self.verticalMoveDirection = direction;
+ self.verticalMoveStartCaretRect = [self caretRectForPosition:start];
+ }
+
+ if (start) {
+
+ UITextPosition *end = [self slk_closestPositionToPosition:start inDirection:direction];
+
+ if (end) {
+ self.verticalMoveLastCaretRect = [self caretRectForPosition:end];
+ self.selectedTextRange = [self textRangeFromPosition:end toPosition:end];
+
+ [self slk_scrollToCaretPositonAnimated:NO];
+ }
+ }
+}
+
+// Based on code from Ruben Cabaco
+// https://gist.github.com/rcabaco/6765778
+
+- (UITextPosition *)slk_closestPositionToPosition:(UITextPosition *)position inDirection:(UITextLayoutDirection)direction
+{
+ // Only up/down are implemented. No real need for left/right since that is native to UITextInput.
+ NSParameterAssert(direction == UITextLayoutDirectionUp || direction == UITextLayoutDirectionDown);
+
+ // Translate the vertical direction to a horizontal direction.
+ UITextLayoutDirection lookupDirection = (direction == UITextLayoutDirectionUp) ? UITextLayoutDirectionLeft : UITextLayoutDirectionRight;
+
+ // Walk one character at a time in `lookupDirection` until the next line is reached.
+ UITextPosition *checkPosition = position;
+ UITextPosition *closestPosition = position;
+ CGRect startingCaretRect = [self caretRectForPosition:position];
+ CGRect nextLineCaretRect;
+ BOOL isInNextLine = NO;
+
+ while (YES) {
+ UITextPosition *nextPosition = [self positionFromPosition:checkPosition inDirection:lookupDirection offset:1];
+
+ // End of line.
+ if (!nextPosition || [self comparePosition:checkPosition toPosition:nextPosition] == NSOrderedSame) {
+ break;
+ }
+
+ checkPosition = nextPosition;
+ CGRect checkRect = [self caretRectForPosition:checkPosition];
+ if (CGRectGetMidY(startingCaretRect) != CGRectGetMidY(checkRect)) {
+ // While on the next line stop just above/below the starting position.
+ if (lookupDirection == UITextLayoutDirectionLeft && CGRectGetMidX(checkRect) <= CGRectGetMidX(self.verticalMoveStartCaretRect)) {
+ closestPosition = checkPosition;
+ break;
+ }
+ if (lookupDirection == UITextLayoutDirectionRight && CGRectGetMidX(checkRect) >= CGRectGetMidX(self.verticalMoveStartCaretRect)) {
+ closestPosition = checkPosition;
+ break;
+ }
+ // But don't skip lines.
+ if (isInNextLine && CGRectGetMidY(checkRect) != CGRectGetMidY(nextLineCaretRect)) {
+ break;
+ }
+
+ isInNextLine = YES;
+ nextLineCaretRect = checkRect;
+ closestPosition = checkPosition;
+ }
+ }
+ return closestPosition;
+}
+
+- (BOOL)slk_isNewVerticalMovementForPosition:(UITextPosition *)position inDirection:(UITextLayoutDirection)direction
+{
+ CGRect caretRect = [self caretRectForPosition:position];
+ BOOL noPreviousStartPosition = CGRectEqualToRect(self.verticalMoveStartCaretRect, CGRectZero);
+ BOOL caretMovedSinceLastPosition = !CGRectEqualToRect(caretRect, self.verticalMoveLastCaretRect);
+ BOOL directionChanged = self.verticalMoveDirection != direction;
+
+ BOOL newMovement = noPreviousStartPosition || caretMovedSinceLastPosition || directionChanged;
+ return newMovement;
+}
+
+
+#pragma mark - Lifeterm
+
+- (void)dealloc
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+
+ [self removeObserver:self forKeyPath:NSStringFromSelector(@selector(contentSize))];
+
+ _placeholderLabel = nil;
+}
+
+@end
diff --git a/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Classes/SLKTextViewController.h b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Classes/SLKTextViewController.h
new file mode 100644
index 000000000..cc7aedb2a
--- /dev/null
+++ b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Classes/SLKTextViewController.h
@@ -0,0 +1,468 @@
+//
+// Copyright 2014 Slack Technologies, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import
+#import
+
+#import "SLKTextInputbar.h"
+#import "SLKTypingIndicatorView.h"
+#import "SLKTextView.h"
+
+#import "SLKTextView+SLKAdditions.h"
+#import "UIScrollView+SLKAdditions.h"
+#import "UIView+SLKAdditions.h"
+
+#import "SLKUIConstants.h"
+
+/**
+ UIKeyboard notification replacement, posting reliably only when showing/hiding the keyboard (not when resizing keyboard, or with inputAccessoryView reloads, etc).
+ Only triggered when using SLKTextViewController's text view.
+ */
+UIKIT_EXTERN NSString *const SLKKeyboardWillShowNotification;
+UIKIT_EXTERN NSString *const SLKKeyboardDidShowNotification;
+UIKIT_EXTERN NSString *const SLKKeyboardWillHideNotification;
+UIKIT_EXTERN NSString *const SLKKeyboardDidHideNotification;
+
+typedef NS_ENUM(NSUInteger, SLKKeyboardStatus) {
+ SLKKeyboardStatusDidHide,
+ SLKKeyboardStatusWillShow,
+ SLKKeyboardStatusDidShow,
+ SLKKeyboardStatusWillHide
+};
+
+/** @name A drop-in UIViewController subclass with a growing text input view and other useful messaging features. */
+NS_CLASS_AVAILABLE_IOS(7_0) @interface SLKTextViewController : UIViewController
+
+/** The main table view managed by the controller object. Created by default initializing with -init or initWithNibName:bundle: */
+@property (nonatomic, readonly) UITableView *tableView;
+
+/** The main collection view managed by the controller object. Not nil if the controller is initialised with -initWithCollectionViewLayout: */
+@property (nonatomic, readonly) UICollectionView *collectionView;
+
+/** The main scroll view managed by the controller object. Not nil if the controller is initialised with -initWithScrollView: */
+@property (nonatomic, readonly) UIScrollView *scrollView;
+
+/** The bottom toolbar containing a text view and buttons. */
+@property (nonatomic, readonly) SLKTextInputbar *textInputbar;
+
+/** The typing indicator used to display user names horizontally. */
+@property (nonatomic, readonly) SLKTypingIndicatorView *typingIndicatorView;
+
+/** A single tap gesture used to dismiss the keyboard. */
+@property (nonatomic, readonly) UIGestureRecognizer *singleTapGesture;
+
+/** A vertical pan gesture used for bringing the keyboard from the bottom. */
+@property (nonatomic, readonly) UIPanGestureRecognizer *verticalPanGesture;
+
+/** YES if control's animation should have bouncy effects. Default is YES. */
+@property (nonatomic, assign) BOOL bounces;
+
+/** YES if text view's content can be cleaned with a shake gesture. Default is NO. */
+@property (nonatomic, assign) BOOL shakeToClearEnabled;
+
+/** YES if keyboard can be dismissed gradually with a vertical panning gesture. Default is YES. */
+@property (nonatomic, assign, getter = isKeyboardPanningEnabled) BOOL keyboardPanningEnabled;
+
+/** YES if an external keyboard has been detected (this value updates only when the text view becomes first responder). */
+@property (nonatomic, readonly) BOOL isExternalKeyboardDetected;
+
+/** YES if after right button press, the text view is cleared out. Default is YES. */
+@property (nonatomic, assign) BOOL shouldClearTextAtRightButtonPress;
+
+/** YES if the text input bar should still move up/down when other text inputs interacts with the keyboard. Default is NO. */
+@property (nonatomic, assign) BOOL shouldForceTextInputbarAdjustment DEPRECATED_MSG_ATTRIBUTE("Use -forceTextInputbarAdjustmentForResponder:");
+
+/** YES if the scrollView should scroll to bottom when the keyboard is shown. Default is NO.*/
+@property (nonatomic, assign) BOOL shouldScrollToBottomAfterKeyboardShows;
+
+/**
+ YES if the main table view is inverted. Default is YES.
+ This allows the table view to start from the bottom like any typical messaging interface.
+ If inverted, you must assign the same transform property to your cells to match the orientation (ie: cell.transform = tableView.transform;)
+ Inverting the table view will enable some great features such as content offset corrections automatically when resizing the text input and/or showing autocompletion.
+
+ Updating this value also changes 'edgesForExtendedLayout' value. When inverted, it must be UIRectEdgeNone, to display correctly all the elements. Otherwise, UIRectEdgeAll is set.
+ */
+@property (nonatomic, assign, getter = isInverted) BOOL inverted;
+
+/** YES if the view controller is presented inside of a popover controller. If YES, the keyboard won't move the text input bar and tapping on the tableView/collectionView will not cause the keyboard to be dismissed. This property is compatible only with iPad. */
+@property (nonatomic, assign, getter = isPresentedInPopover) BOOL presentedInPopover;
+
+/** Convenience accessors (accessed through the text input bar) */
+@property (nonatomic, readonly) SLKTextView *textView;
+@property (nonatomic, readonly) UIButton *leftButton;
+@property (nonatomic, readonly) UIButton *rightButton;
+
+
+#pragma mark - Initialization
+///------------------------------------------------
+/// @name Initialization
+///------------------------------------------------
+
+/**
+ Initializes a text view controller to manage a table view of a given style.
+ If you use the standard -init method, a table view with plain style will be created.
+
+ @param style A constant that specifies the style of main table view that the controller object is to manage (UITableViewStylePlain or UITableViewStyleGrouped).
+ @return An initialized SLKTextViewController object or nil if the object could not be created.
+ */
+- (instancetype)initWithTableViewStyle:(UITableViewStyle)style SLK_DESIGNATED_INITIALIZER;
+
+/**
+ Initializes a collection view controller and configures the collection view with the provided layout.
+ If you use the standard -init method, a table view with plain style will be created.
+
+ @param layout The layout object to associate with the collection view. The layout controls how the collection view presents its cells and supplementary views.
+ @return An initialized SLKTextViewController object or nil if the object could not be created.
+ */
+- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout SLK_DESIGNATED_INITIALIZER;
+
+/**
+ Initializes a text view controller to manage an arbitraty scroll view. The caller is responsible for configuration of the scroll view, including wiring the delegate.
+
+ @param a UISCrollView to be used as the main content area.
+ @return An initialized SLKTextViewController object or nil if the object could not be created.
+ */
+- (instancetype)initWithScrollView:(UIScrollView *)scrollView SLK_DESIGNATED_INITIALIZER;
+
+/**
+ Initializes either a table or collection view controller.
+ You must override either +tableViewStyleForCoder: or +collectionViewLayoutForCoder: to define witch view to be layed out.
+
+ @param decoder An unarchiver object.
+ @return An initialized SLKTextViewController object or nil if the object could not be created.
+ */
+- (instancetype)initWithCoder:(NSCoder *)decoder SLK_DESIGNATED_INITIALIZER;
+
+/**
+ Returns the tableView style to be configured when using Interface Builder. Default is UITableViewStylePlain.
+ You must override this method if you want to configure a tableView.
+
+ @param decoder An unarchiver object.
+ @return The tableView style to be used in the new instantiated tableView.
+ */
++ (UITableViewStyle)tableViewStyleForCoder:(NSCoder *)decoder;
+
+/**
+ Returns the tableView style to be configured when using Interface Builder. Default is nil.
+ You must override this method if you want to configure a collectionView.
+
+ @param decoder An unarchiver object.
+ @return The collectionView style to be used in the new instantiated collectionView.
+ */
++ (UICollectionViewLayout *)collectionViewLayoutForCoder:(NSCoder *)decoder;
+
+
+#pragma mark - Keyboard Handling
+///------------------------------------------------
+/// @name Keyboard Handling
+///------------------------------------------------
+
+/**
+ Presents the keyboard, if not already, animated.
+
+ @param animated YES if the keyboard should show using an animation.
+ */
+- (void)presentKeyboard:(BOOL)animated;
+
+/**
+ Dimisses the keyboard, if not already, animated.
+
+ @param animated YES if the keyboard should be dismissed using an animation.
+ */
+- (void)dismissKeyboard:(BOOL)animated;
+
+/**
+ Verifies if the text input bar should still move up/down even if it is not first responder. Default is NO.
+ You can override this method to perform additional tasks associated with presenting the view. You don't need call super since this method doesn't do anything.
+
+ @param responder The current first responder object.
+ @return YES so the text input bar still move up/down.
+ */
+- (BOOL)forceTextInputbarAdjustmentForResponder:(UIResponder *)responder;
+
+/**
+ Notifies the view controller that the keyboard changed status.
+ You can override this method to perform additional tasks associated with presenting the view. You don't need call super since this method doesn't do anything.
+
+ @param status The new keyboard status.
+ */
+- (void)didChangeKeyboardStatus:(SLKKeyboardStatus)status;
+
+
+#pragma mark - Interaction Notifications
+///------------------------------------------------
+/// @name Interaction Notifications
+///------------------------------------------------
+
+/**
+ Notifies the view controller that the text will update.
+ You can override this method to perform additional tasks associated with text changes. You MUST call super at some point in your implementation.
+ */
+- (void)textWillUpdate NS_REQUIRES_SUPER;
+
+/**
+ Notifies the view controller that the text did update.
+ You can override this method to perform additional tasks associated with text changes. You MUST call super at some point in your implementation.
+
+ @param If YES, the text input bar will be resized using an animation.
+ */
+- (void)textDidUpdate:(BOOL)animated NS_REQUIRES_SUPER;
+
+/**
+ Notifies the view controller that the text selection did change.
+ Use this method a replacement of UITextViewDelegate's -textViewDidChangeSelection: which is not reliable enough when using third-party keyboards (they don't forward events properly sometimes).
+
+ You can override this method to perform additional tasks associated with text changes. You MUST call super at some point in your implementation.
+ */
+- (void)textSelectionDidChange NS_REQUIRES_SUPER;
+
+/**
+ Notifies the view controller when the left button's action has been triggered, manually.
+ You can override this method to perform additional tasks associated with the left button. You don't need call super since this method doesn't do anything.
+
+ @param sender The object calling this method.
+ */
+- (void)didPressLeftButton:(id)sender;
+
+/**
+ Notifies the view controller when the right button's action has been triggered, manually or by using the keyboard return key.
+ You can override this method to perform additional tasks associated with the right button. You MUST call super at some point in your implementation.
+
+ @param sender The object calling this method.
+ */
+- (void)didPressRightButton:(id)sender NS_REQUIRES_SUPER;
+
+/**
+ Verifies if the right button can be pressed. If NO, the button is disabled.
+ You can override this method to perform additional tasks. You SHOULD call super to inherit some conditionals.
+
+ @return YES if the right button can be pressed.
+ */
+- (BOOL)canPressRightButton;
+
+/**
+ Notifies the view controller when the user has pasted a supported media content (images and/or videos).
+ You can override this method to perform additional tasks associated with image/video pasting. You don't need to call super since this method doesn't do anything.
+ Only supported pastable medias configured in SLKTextView will be forwarded (take a look at SLKPastableMediaType).
+
+ @para userInfo The payload containing the media data, content and media types.
+ */
+- (void)didPasteMediaContent:(NSDictionary *)userInfo;
+
+/**
+ Verifies that the typing indicator view should be shown. Default is YES, if meeting some requierements.
+ You can override this method to perform additional tasks. You SHOULD call super to inherit some conditionals.
+
+ @return YES if the typing indicator view should be shown.
+ */
+- (BOOL)canShowTypeIndicator;
+
+/**
+ Notifies the view controller when the user has shaked the device for undoing text typing.
+ You can override this method to perform additional tasks associated with the shake gesture. Calling super will prompt a system alert view with undo option. This will not be called if 'undoShakingEnabled' is set to NO and/or if the text view's content is empty.
+ */
+- (void)willRequestUndo;
+
+/**
+ Notifies the view controller when the user has pressed the Return key (↵) with an external keyboard.
+ You can override this method to perform additional tasks. You MUST call super at some point in your implementation.
+ */
+- (void)didPressReturnKey:(id)sender NS_REQUIRES_SUPER;
+
+/**
+ Notifies the view controller when the user has pressed the Escape key (Esc) with an external keyboard.
+ You can override this method to perform additional tasks. You MUST call super at some point in your implementation.
+ */
+- (void)didPressEscapeKey:(id)sender NS_REQUIRES_SUPER;
+
+/**
+ Notifies the view controller when the user has pressed the arrow key with an external keyboard.
+ You can override this method to perform additional tasks. You MUST call super at some point in your implementation.
+ */
+- (void)didPressArrowKey:(id)sender NS_REQUIRES_SUPER;
+
+
+#pragma mark - Text Edition
+///------------------------------------------------
+/// @name Text Edition
+///------------------------------------------------
+
+/** YES if the text editing mode is active. */
+@property (nonatomic, readonly, getter = isEditing) BOOL editing;
+
+/**
+ Re-uses the text layout for edition, displaying an accessory view on top of the text input bar with options (cancel & save).
+ You can override this method to perform additional tasks. You MUST call super at some point in your implementation.
+
+ @param text The string text to edit.
+ */
+- (void)editText:(NSString *)text NS_REQUIRES_SUPER;
+
+/**
+ Notifies the view controller when the editing bar's right button's action has been triggered, manually or by using the external keyboard's Return key.
+ You can override this method to perform additional tasks associated with accepting changes. You MUST call super at some point in your implementation.
+
+ @param sender The object calling this method.
+ */
+- (void)didCommitTextEditing:(id)sender NS_REQUIRES_SUPER;
+
+/**
+ Notifies the view controller when the editing bar's right button's action has been triggered, manually or by using the external keyboard's Esc key.
+ You can override this method to perform additional tasks associated with accepting changes. You MUST call super at some point in your implementation.
+
+ @param sender The object calling this method.
+ */
+- (void)didCancelTextEditing:(id)sender NS_REQUIRES_SUPER;
+
+
+#pragma mark - Text Auto-Completion
+///------------------------------------------------
+/// @name Text Auto-Completion
+///------------------------------------------------
+
+/** The table view used to display autocompletion results. */
+@property (nonatomic, readonly) UITableView *autoCompletionView;
+
+/** The recently found prefix symbol used as prefix for autocompletion mode. */
+@property (nonatomic, readonly, copy) NSString *foundPrefix;
+
+/** The range of the found prefix in the text view content. */
+@property (nonatomic, readonly) NSRange foundPrefixRange;
+
+/** The recently found word at the text view's caret position. */
+@property (nonatomic, readonly, copy) NSString *foundWord;
+
+/** YES if the autocompletion mode is active. */
+@property (nonatomic, readonly, getter = isAutoCompleting) BOOL autoCompleting;
+
+/** An array containing all the registered prefix strings for autocompletion. */
+@property (nonatomic, readonly, copy) NSArray *registeredPrefixes;
+
+/**
+ Registers any string prefix for autocompletion detection, useful for user mentions and/or hashtags autocompletion.
+ The prefix must be valid NSString (i.e: '@', '#', '\', and so on). This also checks if no repeated prefix is inserted.
+
+ @param prefixes An array of prefix strings.
+ */
+- (void)registerPrefixesForAutoCompletion:(NSArray *)prefixes;
+
+/**
+ Verifies that the autocompletion view should be shown. Default is NO.
+ To enabled autocompletion, MUST override this method to perform additional tasks, before the autocompletion view is shown (i.e. populating the data source).
+
+ @return YES if the autocompletion view should be shown.
+ */
+- (BOOL)canShowAutoCompletion;
+
+/**
+ Returns a custom height for the autocompletion view. Default is 0.0.
+ You can override this method to return a custom height.
+
+ @return The autocompletion view's height.
+ */
+- (CGFloat)heightForAutoCompletionView;
+
+/**
+ Returns the maximum height for the autocompletion view. Default is 140 pts.
+ You can override this method to return a custom max height.
+
+ @return The autocompletion view's max height.
+ */
+- (CGFloat)maximumHeightForAutoCompletionView;
+
+/**
+ Cancels and hides the autocompletion view, animated.
+ */
+- (void)cancelAutoCompletion;
+
+/**
+ Accepts the autocompletion, replacing the detected word with a new string, keeping the prefix.
+ This method is an abstraction of -acceptAutoCompletionWithString:keepPrefix:
+
+ @param string The string to be used for replacing autocompletion placeholders.
+ */
+- (void)acceptAutoCompletionWithString:(NSString *)string;
+
+/**
+ Accepts the autocompletion, replacing the detected word with a new string, and optionally replacing the prefix too.
+
+ @param string The string to be used for replacing autocompletion placeholders.
+ @param keepPrefix YES if the prefix shouldn't be replaced.
+ */
+- (void)acceptAutoCompletionWithString:(NSString *)string keepPrefix:(BOOL)keepPrefix;
+
+
+#pragma mark - Text Caching
+///------------------------------------------------
+/// @name Text Caching
+///------------------------------------------------
+
+/**
+ Returns the key to be associated with a given text to be cached. Default is nil.
+ To enable text caching, you must override this method to return valid key.
+ The text view will be populated automatically when the view controller is configured.
+ You don't need call super since this method doesn't do anything.
+
+ @return The key for which to enable text caching.
+ */
+- (id)keyForTextCaching;
+
+/**
+ Removes the current's vien controller cached text.
+ To enable this, you must return a valid key string in -keyForTextCaching.
+ */
+- (void)clearCachedText;
+
+/**
+ Removes all the cached text from disk.
+ */
++ (void)clearAllCachedText;
+
+
+#pragma mark - Customization
+///------------------------------------------------
+/// @name Customization
+///------------------------------------------------
+
+/**
+ Registers a class for customizing the behavior and appearance of the text view.
+ You need to call this method inside of any initialization method.
+
+ @param textViewClass A SLKTextView subclass.
+ */
+- (void)registerClassForTextView:(Class)textViewClass;
+
+
+#pragma mark - Delegate Methods Requiring Super
+///------------------------------------------------
+/// @name Delegate Methods Requiring Super
+///------------------------------------------------
+
+/** UITextViewDelegate */
+- (BOOL)textView:(SLKTextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text NS_REQUIRES_SUPER;
+
+/** UIScrollViewDelegate */
+- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView NS_REQUIRES_SUPER;
+- (void)scrollViewDidScroll:(UIScrollView *)scrollView NS_REQUIRES_SUPER;
+
+/** UIGestureRecognizerDelegate */
+- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer NS_REQUIRES_SUPER;
+
+/** UIAlertViewDelegate */
+- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex NS_REQUIRES_SUPER;
+
+@end
diff --git a/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Classes/SLKTextViewController.m b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Classes/SLKTextViewController.m
new file mode 100644
index 000000000..677d0267c
--- /dev/null
+++ b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Classes/SLKTextViewController.m
@@ -0,0 +1,1956 @@
+//
+// Copyright 2014 Slack Technologies, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import "SLKTextViewController.h"
+#import "SLKInputAccessoryView.h"
+#import "UIResponder+SLKAdditions.h"
+#import "SLKUIConstants.h"
+
+NSString * const SLKKeyboardWillShowNotification = @"SLKKeyboardWillShowNotification";
+NSString * const SLKKeyboardDidShowNotification = @"SLKKeyboardDidShowNotification";
+NSString * const SLKKeyboardWillHideNotification = @"SLKKeyboardWillHideNotification";
+NSString * const SLKKeyboardDidHideNotification = @"SLKKeyboardDidHideNotification";
+
+NSInteger const SLKAlertViewClearTextTag = 1534347677; // absolute hash of 'SLKTextViewController' string
+
+@interface SLKTextViewController ()
+{
+ CGPoint _scrollViewOffsetBeforeDragging;
+ CGFloat _keyboardHeightBeforeDragging;
+}
+
+// The shared scrollView pointer, either a tableView or collectionView
+@property (nonatomic, weak) UIScrollView *scrollViewProxy;
+
+// A hairline displayed on top of the auto-completion view, to better separate the content from the control.
+@property (nonatomic, strong) UIView *autoCompletionHairline;
+
+// Auto-Layout height constraints used for updating their constants
+@property (nonatomic, strong) NSLayoutConstraint *scrollViewHC;
+@property (nonatomic, strong) NSLayoutConstraint *textInputbarHC;
+@property (nonatomic, strong) NSLayoutConstraint *typingIndicatorViewHC;
+@property (nonatomic, strong) NSLayoutConstraint *autoCompletionViewHC;
+@property (nonatomic, strong) NSLayoutConstraint *keyboardHC;
+
+// The keyboard commands available for external keyboards
+@property (nonatomic, strong) NSArray *keyboardCommands;
+
+// YES if the user is moving the keyboard with a gesture
+@property (nonatomic, assign, getter = isMovingKeyboard) BOOL movingKeyboard;
+
+// The setter of isExternalKeyboardDetected, for private use.
+@property (nonatomic, assign) BOOL externalKeyboardDetected;
+
+// The current keyboard status (hidden, showing, etc.)
+@property (nonatomic) SLKKeyboardStatus keyboardStatus;
+
+// YES if a new word has been typed recently
+@property (nonatomic) BOOL newWordInserted;
+
+// YES if the view controller did appear and everything is finished configurating. This allows blocking some layout animations among other things.
+@property (nonatomic, getter=isViewVisible) BOOL viewVisible;
+
+// The setter of isExternalKeyboardDetected, for private use.
+@property (nonatomic, getter = isRotating) BOOL rotating;
+
+// The subclass of SLKTextView class to use
+@property (nonatomic, strong) Class textViewClass;
+
+@end
+
+@implementation SLKTextViewController
+@synthesize tableView = _tableView;
+@synthesize collectionView = _collectionView;
+@synthesize scrollView = _scrollView;
+@synthesize typingIndicatorView = _typingIndicatorView;
+@synthesize textInputbar = _textInputbar;
+@synthesize autoCompletionView = _autoCompletionView;
+@synthesize autoCompleting = _autoCompleting;
+@synthesize scrollViewProxy = _scrollViewProxy;
+@synthesize presentedInPopover = _presentedInPopover;
+
+#pragma mark - Initializer
+
+- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
+{
+ return [self initWithTableViewStyle:UITableViewStylePlain];
+}
+
+- (instancetype)init
+{
+ return [self initWithTableViewStyle:UITableViewStylePlain];
+}
+
+- (instancetype)initWithTableViewStyle:(UITableViewStyle)style
+{
+ NSAssert([self class] != [SLKTextViewController class], @"Oops! You must subclass SLKTextViewController.");
+
+ if (self = [super initWithNibName:nil bundle:nil])
+ {
+ self.scrollViewProxy = [self tableViewWithStyle:style];
+ [self slk_commonInit];
+ }
+ return self;
+}
+
+- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout
+{
+ NSAssert([self class] != [SLKTextViewController class], @"Oops! You must subclass SLKTextViewController.");
+
+ if (self = [super initWithNibName:nil bundle:nil])
+ {
+ self.scrollViewProxy = [self collectionViewWithLayout:layout];
+ [self slk_commonInit];
+ }
+ return self;
+}
+
+- (instancetype)initWithScrollView:(UIScrollView *)scrollView
+{
+ NSAssert([self class] != [SLKTextViewController class], @"Oops! You must subclass SLKTextViewController.");
+
+ if (self = [super initWithNibName:nil bundle:nil])
+ {
+ _scrollView = scrollView;
+ _scrollView.translatesAutoresizingMaskIntoConstraints = NO; // Makes sure the scrollView plays nice with auto-layout
+
+ self.scrollViewProxy = _scrollView;
+ [self slk_commonInit];
+ }
+ return self;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)decoder
+{
+ NSAssert([self class] != [SLKTextViewController class], @"Oops! You must subclass SLKTextViewController.");
+
+ if (self = [super initWithCoder:decoder])
+ {
+ UITableViewStyle tableViewStyle = [[self class] tableViewStyleForCoder:decoder];
+ UICollectionViewLayout *collectionViewLayout = [[self class] collectionViewLayoutForCoder:decoder];
+
+ if ([collectionViewLayout isKindOfClass:[UICollectionViewLayout class]]) {
+ self.scrollViewProxy = [self collectionViewWithLayout:collectionViewLayout];
+ }
+ else {
+ self.scrollViewProxy = [self tableViewWithStyle:tableViewStyle];
+ }
+
+ [self slk_commonInit];
+ }
+ return self;
+}
+
+- (void)slk_commonInit
+{
+ [self slk_registerNotifications];
+
+ self.bounces = YES;
+ self.inverted = YES;
+ self.shakeToClearEnabled = NO;
+ self.keyboardPanningEnabled = YES;
+ self.shouldClearTextAtRightButtonPress = YES;
+ self.shouldScrollToBottomAfterKeyboardShows = NO;
+}
+
+
+#pragma mark - View lifecycle
+
+- (void)viewDidLoad
+{
+ [super viewDidLoad];
+
+ [self.view addSubview:self.scrollViewProxy];
+ [self.view addSubview:self.autoCompletionView];
+ [self.view addSubview:self.typingIndicatorView];
+ [self.view addSubview:self.textInputbar];
+
+ [self slk_setupViewConstraints];
+}
+
+- (void)viewWillAppear:(BOOL)animated
+{
+ [super viewWillAppear:animated];
+
+ // Invalidates this flag when the view appears
+ self.textView.didNotResignFirstResponder = NO;
+
+ [UIView performWithoutAnimation:^{
+ // Reloads any cached text
+ [self slk_reloadTextView];
+ }];
+
+ // Helps laying out subviews with recently added constraints.
+ [self.view layoutIfNeeded];
+}
+
+- (void)viewDidAppear:(BOOL)animated
+{
+ [super viewDidAppear:animated];
+
+ [self.scrollViewProxy flashScrollIndicators];
+
+ self.viewVisible = YES;
+}
+
+- (void)viewWillDisappear:(BOOL)animated
+{
+ [super viewWillDisappear:animated];
+
+ // Stops the keyboard from being dismissed during the navigation controller's "swipe-to-pop"
+ self.textView.didNotResignFirstResponder = self.isMovingFromParentViewController;
+
+ self.viewVisible = NO;
+
+ // Caches the text before it's too late!
+ [self slk_cacheTextView];
+}
+
+- (void)viewDidDisappear:(BOOL)animated
+{
+ [super viewDidDisappear:animated];
+}
+
+- (void)viewWillLayoutSubviews
+{
+ [super viewWillLayoutSubviews];
+
+ [self slk_adjustContentConfigurationIfNeeded];
+}
+
+- (void)viewDidLayoutSubviews
+{
+ [super viewDidLayoutSubviews];
+}
+
+
+#pragma mark - Getters
+
++ (UITableViewStyle)tableViewStyleForCoder:(NSCoder *)decoder
+{
+ return UITableViewStylePlain;
+}
+
++ (UICollectionViewLayout *)collectionViewLayoutForCoder:(NSCoder *)decoder
+{
+ return nil;
+}
+
+- (UITableView *)tableViewWithStyle:(UITableViewStyle)style
+{
+ if (!_tableView)
+ {
+ _tableView = [[UITableView alloc] initWithFrame:CGRectZero style:style];
+ _tableView.translatesAutoresizingMaskIntoConstraints = NO;
+ _tableView.backgroundColor = [UIColor whiteColor];
+ _tableView.scrollsToTop = YES;
+ _tableView.dataSource = self;
+ _tableView.delegate = self;
+ }
+ return _tableView;
+}
+
+- (UICollectionView *)collectionViewWithLayout:(UICollectionViewLayout *)layout
+{
+ if (!_collectionView)
+ {
+ _collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
+ _collectionView.translatesAutoresizingMaskIntoConstraints = NO;
+ _collectionView.backgroundColor = [UIColor whiteColor];
+ _collectionView.scrollsToTop = YES;
+ _collectionView.dataSource = self;
+ _collectionView.delegate = self;
+ }
+ return _collectionView;
+}
+
+- (UITableView *)autoCompletionView
+{
+ if (!_autoCompletionView)
+ {
+ _autoCompletionView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
+ _autoCompletionView.translatesAutoresizingMaskIntoConstraints = NO;
+ _autoCompletionView.backgroundColor = [UIColor colorWithWhite:0.97 alpha:1.0];
+ _autoCompletionView.scrollsToTop = NO;
+ _autoCompletionView.dataSource = self;
+ _autoCompletionView.delegate = self;
+
+ CGRect rect = CGRectZero;
+ rect.size = CGSizeMake(CGRectGetWidth(self.view.frame), 0.5);
+
+ _autoCompletionHairline = [[UIView alloc] initWithFrame:rect];
+ _autoCompletionHairline.autoresizingMask = UIViewAutoresizingFlexibleWidth;
+ _autoCompletionHairline.backgroundColor = _autoCompletionView.separatorColor;
+ [_autoCompletionView addSubview:_autoCompletionHairline];
+ }
+ return _autoCompletionView;
+}
+
+- (SLKTextInputbar *)textInputbar
+{
+ if (!_textInputbar)
+ {
+ _textInputbar = [[SLKTextInputbar alloc] initWithTextViewClass:self.textViewClass];
+ _textInputbar.translatesAutoresizingMaskIntoConstraints = NO;
+ _textInputbar.controller = self;
+
+ [_textInputbar.leftButton addTarget:self action:@selector(didPressLeftButton:) forControlEvents:UIControlEventTouchUpInside];
+ [_textInputbar.rightButton addTarget:self action:@selector(didPressRightButton:) forControlEvents:UIControlEventTouchUpInside];
+ [_textInputbar.editortLeftButton addTarget:self action:@selector(didCancelTextEditing:) forControlEvents:UIControlEventTouchUpInside];
+ [_textInputbar.editortRightButton addTarget:self action:@selector(didCommitTextEditing:) forControlEvents:UIControlEventTouchUpInside];
+
+ _textInputbar.textView.delegate = self;
+
+ _verticalPanGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(slk_didPanTextView:)];
+ _verticalPanGesture.delegate = self;
+
+ [_textInputbar.textView addGestureRecognizer:self.verticalPanGesture];
+ }
+ return _textInputbar;
+}
+
+- (SLKTypingIndicatorView *)typingIndicatorView
+{
+ if (!_typingIndicatorView)
+ {
+ _typingIndicatorView = [SLKTypingIndicatorView new];
+ _typingIndicatorView.translatesAutoresizingMaskIntoConstraints = NO;
+ _typingIndicatorView.canResignByTouch = NO;
+ }
+ return _typingIndicatorView;
+}
+
+- (BOOL)isExternalKeyboardDetected
+{
+ return _externalKeyboardDetected;
+}
+
+- (BOOL)isPresentedInPopover
+{
+ return _presentedInPopover && SLK_IS_IPAD;
+}
+
+- (SLKTextView *)textView
+{
+ return self.textInputbar.textView;
+}
+
+- (UIButton *)leftButton
+{
+ return self.textInputbar.leftButton;
+}
+
+- (UIButton *)rightButton
+{
+ return self.textInputbar.rightButton;
+}
+
+- (SLKInputAccessoryView *)emptyInputAccessoryView
+{
+ if (!self.isKeyboardPanningEnabled) {
+ return nil;
+ }
+
+ SLKInputAccessoryView *view = [[SLKInputAccessoryView alloc] initWithFrame:self.textInputbar.bounds];
+ view.backgroundColor = [UIColor clearColor];
+ view.userInteractionEnabled = NO;
+
+#if SLK_INPUT_ACCESSORY_DEBUG
+ view.backgroundColor = [[UIColor redColor] colorWithAlphaComponent:0.5];
+#endif
+
+ return view;
+}
+
+- (UIModalPresentationStyle)modalPresentationStyle
+{
+ if (self.navigationController) {
+ return self.navigationController.modalPresentationStyle;
+ }
+ return [super modalPresentationStyle];
+}
+
+- (CGFloat)slk_appropriateKeyboardHeight:(NSNotification *)notification
+{
+ self.externalKeyboardDetected = [self slk_detectExternalKeyboardInNotification:notification];
+ if (self.externalKeyboardDetected) {
+ return 0.0;
+ }
+
+ CGRect endFrame = [self.view convertRect:[notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue] fromView:nil];
+ return MAX(0.0, CGRectGetHeight(self.view.bounds) - CGRectGetMinY(endFrame) - CGRectGetHeight(self.textView.inputAccessoryView.bounds));
+}
+
+- (CGFloat)slk_appropriateScrollViewHeight
+{
+ CGFloat height = self.view.bounds.size.height;
+
+ height -= self.keyboardHC.constant;
+ height -= self.textInputbarHC.constant;
+ height -= self.autoCompletionViewHC.constant;
+ height -= self.typingIndicatorViewHC.constant;
+
+ if (height < 0) return 0;
+ else return roundf(height);
+}
+
+- (CGFloat)slk_topBarsHeight
+{
+ // No need to adjust if the edge isn't available
+ if ((self.edgesForExtendedLayout & UIRectEdgeTop) == 0) {
+ return 0.0;
+ }
+
+ CGFloat height = CGRectGetHeight(self.navigationController.navigationBar.frame);
+
+ if (SLK_IS_IPHONE && SLK_IS_LANDSCAPE && SLK_IS_IOS8_AND_HIGHER) {
+ return height;
+ }
+ if (SLK_IS_IPAD && self.modalPresentationStyle == UIModalPresentationFormSheet) {
+ return height;
+ }
+ if (self.isPresentedInPopover) {
+ return height;
+ }
+
+ height += CGRectGetHeight([UIApplication sharedApplication].statusBarFrame);
+ return height;
+}
+
+- (NSString *)slk_appropriateKeyboardNotificationName:(NSNotification *)notification
+{
+ NSString *name = notification.name;
+
+ if ([name isEqualToString:UIKeyboardWillShowNotification]) {
+ return SLKKeyboardWillShowNotification;
+ }
+ if ([name isEqualToString:UIKeyboardWillHideNotification]) {
+ return SLKKeyboardWillHideNotification;
+ }
+ if ([name isEqualToString:UIKeyboardDidShowNotification]) {
+ return SLKKeyboardDidShowNotification;
+ }
+ if ([name isEqualToString:UIKeyboardDidHideNotification]) {
+ return SLKKeyboardDidHideNotification;
+ }
+ return nil;
+}
+
+- (SLKKeyboardStatus)slk_keyboardStatusForNotification:(NSNotification *)notification
+{
+ NSString *name = notification.name;
+
+ if ([name isEqualToString:UIKeyboardWillShowNotification]) {
+ return SLKKeyboardStatusWillShow;
+ }
+ if ([name isEqualToString:UIKeyboardDidShowNotification]) {
+ return SLKKeyboardStatusDidShow;
+ }
+ if ([name isEqualToString:UIKeyboardWillHideNotification]) {
+ return SLKKeyboardStatusWillHide;
+ }
+ if ([name isEqualToString:UIKeyboardDidHideNotification]) {
+ return SLKKeyboardStatusDidHide;
+ }
+ return -1;
+}
+
+- (BOOL)slk_isIllogicalKeyboardStatus:(SLKKeyboardStatus)status
+{
+ if ((self.keyboardStatus == 0 && status == 1) ||
+ (self.keyboardStatus == 1 && status == 2) ||
+ (self.keyboardStatus == 2 && status == 3) ||
+ (self.keyboardStatus == 3 && status == 0)) {
+ return NO;
+ }
+ return YES;
+}
+
+
+#pragma mark - Setters
+
+- (void)setScrollViewProxy:(UIScrollView *)scrollView
+{
+ if ([_scrollViewProxy isEqual:scrollView]) {
+ return;
+ }
+
+ _singleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(slk_didTapScrollView:)];
+ _singleTapGesture.delegate = self;
+ [_singleTapGesture requireGestureRecognizerToFail:scrollView.panGestureRecognizer];
+
+ [scrollView addGestureRecognizer:self.singleTapGesture];
+
+ _scrollViewProxy = scrollView;
+}
+
+- (void)setAutoCompleting:(BOOL)autoCompleting
+{
+ if (_autoCompleting == autoCompleting) {
+ return;
+ }
+
+ _autoCompleting = autoCompleting;
+
+ self.scrollViewProxy.scrollEnabled = !autoCompleting;
+}
+
+- (void)setInverted:(BOOL)inverted
+{
+ if (_inverted == inverted) {
+ return;
+ }
+
+ _inverted = inverted;
+
+ self.scrollViewProxy.transform = inverted ? CGAffineTransformMake(1, 0, 0, -1, 0, 0) : CGAffineTransformIdentity;
+ self.automaticallyAdjustsScrollViewInsets = inverted ? NO : YES;
+}
+
+- (void)setKeyboardPanningEnabled:(BOOL)enabled
+{
+ if (_keyboardPanningEnabled == enabled) {
+ return;
+ }
+
+ _keyboardPanningEnabled = enabled;
+
+ self.scrollViewProxy.keyboardDismissMode = enabled ? UIScrollViewKeyboardDismissModeInteractive : UIScrollViewKeyboardDismissModeNone;
+}
+
+- (BOOL)slk_updateKeyboardStatus:(SLKKeyboardStatus)status
+{
+ // Skips if trying to update the same status
+ if (_keyboardStatus == status) {
+ return NO;
+ }
+
+ // Skips illogical conditions
+ if ([self slk_isIllogicalKeyboardStatus:status]) {
+ return NO;
+ }
+
+ _keyboardStatus = status;
+
+ [self didChangeKeyboardStatus:status];
+
+ return YES;
+}
+
+
+#pragma mark - Public & Subclassable Methods
+
+- (void)presentKeyboard:(BOOL)animated
+{
+ // Skips if already first responder
+ if ([self.textView isFirstResponder]) {
+ return;
+ }
+
+ if (!animated) {
+ [UIView performWithoutAnimation:^{
+ [self.textView becomeFirstResponder];
+ }];
+ }
+ else {
+ [self.textView becomeFirstResponder];
+ }
+}
+
+- (void)dismissKeyboard:(BOOL)animated
+{
+ if (![self.textView isFirstResponder]) {
+
+ // Dismisses the keyboard from any first responder in the window.
+ if (self.keyboardHC.constant > 0) {
+ [self.view.window endEditing:NO];
+ }
+ return;
+ }
+
+ if (!animated)
+ {
+ [UIView performWithoutAnimation:^{
+ [self.textView resignFirstResponder];
+ }];
+ }
+ else {
+ [self.textView resignFirstResponder];
+ }
+}
+
+- (BOOL)forceTextInputbarAdjustmentForResponder:(UIResponder *)responder
+{
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+ return self.shouldForceTextInputbarAdjustment;
+#pragma GCC diagnostic pop
+}
+
+- (void)didChangeKeyboardStatus:(SLKKeyboardStatus)status
+{
+ // No implementation here. Meant to be overriden in subclass.
+}
+
+- (void)textWillUpdate
+{
+ // No implementation here. Meant to be overriden in subclass.
+}
+
+- (void)textDidUpdate:(BOOL)animated
+{
+ CGFloat inputbarHeight = self.textInputbar.appropriateHeight;
+
+ self.textInputbar.rightButton.enabled = [self canPressRightButton];
+ self.textInputbar.editortRightButton.enabled = [self canPressRightButton];
+
+ if (inputbarHeight != self.textInputbarHC.constant)
+ {
+ self.textInputbarHC.constant = inputbarHeight;
+ self.scrollViewHC.constant = [self slk_appropriateScrollViewHeight];
+
+ if (animated) {
+
+ BOOL bounces = self.bounces && [self.textView isFirstResponder];
+
+ [self.view slk_animateLayoutIfNeededWithBounce:bounces
+ options:UIViewAnimationOptionCurveEaseInOut|UIViewAnimationOptionLayoutSubviews|UIViewAnimationOptionBeginFromCurrentState
+ animations:^{
+ if (self.textInputbar.isEditing) {
+ [self.textView slk_scrollToCaretPositonAnimated:NO];
+ }
+ }];
+ }
+ else {
+ [self.view layoutIfNeeded];
+ }
+ }
+
+ // Only updates the input view if the number of line changed
+ [self slk_reloadInputAccessoryViewIfNeeded];
+
+ // Toggles auto-correction if requiered
+ [self slk_enableTypingSuggestionIfNeeded];
+}
+
+- (void)textSelectionDidChange
+{
+ // The text view must be first responder
+ if (![self.textView isFirstResponder]) {
+ return;
+ }
+
+ // Skips if the loupe is visible or if there is a real text selection
+ if (self.textView.isLoupeVisible || self.textView.selectedRange.length > 0) {
+ return;
+ }
+
+ // Process the text at every caret movement
+ [self slk_processTextForAutoCompletion];
+}
+
+- (BOOL)canPressRightButton
+{
+ NSString *text = [self.textView.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
+
+ if (text.length > 0 && ![self.textInputbar limitExceeded]) {
+ return YES;
+ }
+
+ return NO;
+}
+
+- (void)didPressLeftButton:(id)sender
+{
+ // No implementation here. Meant to be overriden in subclass.
+}
+
+- (void)didPressRightButton:(id)sender
+{
+ if (self.shouldClearTextAtRightButtonPress) {
+ // Clears the text and the undo manager
+ [self.textView slk_clearText:YES];
+ }
+
+ // Clears cache
+ [self clearCachedText];
+}
+
+- (void)editText:(NSString *)text
+{
+ if (![self.textInputbar canEditText:text]) {
+ return;
+ }
+
+ // Caches the current text, in case the user cancels the edition
+ [self slk_cacheTextToDisk:self.textView.text];
+
+ if (!SLK_IS_LANDSCAPE) {
+ [self.textView setText:text];
+ }
+
+ [self.textInputbar beginTextEditing];
+
+ // Setting the text after calling -beginTextEditing is safer when in landscape orientation
+ if (SLK_IS_LANDSCAPE) {
+ [self.textView setText:text];
+ }
+
+ [self.textView slk_scrollToCaretPositonAnimated:YES];
+
+ // Brings up the keyboard if needed
+ [self presentKeyboard:YES];
+}
+
+- (void)didCommitTextEditing:(id)sender
+{
+ if (!self.textInputbar.isEditing) {
+ return;
+ }
+
+ [self.textInputbar endTextEdition];
+
+ // Clears the text and but not the undo manager
+ [self.textView slk_clearText:NO];
+}
+
+- (void)didCancelTextEditing:(id)sender
+{
+ if (!self.textInputbar.isEditing) {
+ return;
+ }
+
+ [self.textInputbar endTextEdition];
+
+ // Clears the text and but not the undo manager
+ [self.textView slk_clearText:NO];
+
+ // Restores any previous cached text before entering in editing mode
+ [self slk_reloadTextView];
+}
+
+- (BOOL)canShowTypeIndicator
+{
+ // Don't show if the text is being edited or auto-completed.
+ if (self.textInputbar.isEditing || self.isAutoCompleting) {
+ return NO;
+ }
+
+ // Don't show if the content offset is not at top (when inverted) or at bottom (when not inverted)
+ if ((self.isInverted && ![self.scrollViewProxy slk_isAtTop]) || (!self.isInverted && ![self.scrollViewProxy slk_isAtBottom])) {
+ return NO;
+ }
+
+ return YES;
+}
+
+- (BOOL)canShowAutoCompletion
+{
+ return NO;
+}
+
+- (CGFloat)heightForAutoCompletionView
+{
+ return 0.0;
+}
+
+- (CGFloat)maximumHeightForAutoCompletionView
+{
+ CGFloat maxiumumHeight = 140.0;
+ CGFloat scrollViewHeight = self.scrollViewHC.constant;
+ scrollViewHeight -= [self slk_topBarsHeight];
+
+ if (scrollViewHeight < maxiumumHeight) {
+ maxiumumHeight = scrollViewHeight;
+ }
+
+ return maxiumumHeight;
+}
+
+- (void)didPasteMediaContent:(NSDictionary *)userInfo
+{
+ // No implementation here. Meant to be overriden in subclass.
+}
+
+- (void)willRequestUndo
+{
+ UIAlertView *alert = [UIAlertView new];
+ [alert setTitle:NSLocalizedString(@"Undo Typing", nil)];
+ [alert addButtonWithTitle:NSLocalizedString(@"Undo", nil)];
+ [alert addButtonWithTitle:NSLocalizedString(@"Cancel", nil)];
+ [alert setCancelButtonIndex:1];
+ [alert setTag:SLKAlertViewClearTextTag];
+ [alert setDelegate:self];
+ [alert show];
+}
+
+
+#pragma mark - Private Methods
+
+- (void)slk_didTapScrollView:(UIGestureRecognizer *)gesture
+{
+ if (!self.isPresentedInPopover && !self.isExternalKeyboardDetected) {
+ [self dismissKeyboard:YES];
+ }
+}
+
+- (void)slk_didPanTextView:(UIGestureRecognizer *)gesture
+{
+ [self presentKeyboard:YES];
+}
+
+- (void)slk_performRightAction
+{
+ NSArray *actions = [self.rightButton actionsForTarget:self forControlEvent:UIControlEventTouchUpInside];
+
+ if (actions.count > 0 && [self canPressRightButton]) {
+ [self.rightButton sendActionsForControlEvents:UIControlEventTouchUpInside];
+ }
+}
+
+- (void)slk_postKeyboarStatusNotification:(NSNotification *)notification
+{
+ if (self.isExternalKeyboardDetected || self.isRotating) {
+ return;
+ }
+
+ NSMutableDictionary *userInfo = [notification.userInfo mutableCopy];
+
+ CGRect beginFrame = [notification.userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue];
+ CGRect endFrame = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
+
+ // Fixes iOS7 oddness with inverted values on landscape orientation
+ if (!SLK_IS_IOS8_AND_HIGHER && SLK_IS_LANDSCAPE) {
+ beginFrame = SLKRectInvert(beginFrame);
+ endFrame = SLKRectInvert(endFrame);
+ }
+
+ CGFloat keyboardHeight = CGRectGetHeight(endFrame)-CGRectGetHeight(self.textView.inputAccessoryView.bounds);
+
+ beginFrame.size.height = keyboardHeight;
+ endFrame.size.height = keyboardHeight;
+
+ [userInfo setObject:[NSValue valueWithCGRect:beginFrame] forKey:UIKeyboardFrameBeginUserInfoKey];
+ [userInfo setObject:[NSValue valueWithCGRect:endFrame] forKey:UIKeyboardFrameEndUserInfoKey];
+
+ NSString *name = [self slk_appropriateKeyboardNotificationName:notification];
+ [[NSNotificationCenter defaultCenter] postNotificationName:name object:self.textView userInfo:userInfo];
+}
+
+- (BOOL)slk_scrollToTopIfNeeded
+{
+ if (!self.scrollViewProxy.scrollsToTop || self.keyboardStatus == SLKKeyboardStatusWillShow) {
+ return NO;
+ }
+
+ if (self.isInverted) {
+ [self.scrollViewProxy slk_scrollToTopAnimated:YES];
+ return NO;
+ }
+ else {
+ return YES;
+ }
+}
+
+- (BOOL)slk_scrollToBottomIfNeeded
+{
+ // Scrolls to bottom only if the keyboard is about to show.
+ if (!self.shouldScrollToBottomAfterKeyboardShows || self.keyboardStatus != SLKKeyboardStatusWillShow) {
+ return NO;
+ }
+
+ if (self.isInverted) {
+ [self.scrollViewProxy slk_scrollToTopAnimated:YES];
+ }
+ else {
+ [self.scrollViewProxy slk_scrollToBottomAnimated:YES];
+ }
+
+ return YES;
+}
+
+- (void)slk_enableTypingSuggestionIfNeeded
+{
+ if (![self.textView isFirstResponder]) {
+ return;
+ }
+
+ BOOL enable = !self.isAutoCompleting;
+
+ // Toggling autocorrect on Japanese keyboards breaks autocompletion by replacing the autocompletion prefix by an empty string.
+ // So for now, let's not disable autocorrection for Japanese.
+ if ([self.textView.textInputMode.primaryLanguage isEqualToString:@"ja-JP"]) {
+ return;
+ }
+
+ // During text autocompletion, the iOS 8 QuickType bar is hidden and auto-correction and spell checking are disabled.
+ [self.textView setTypingSuggestionEnabled:enable];
+}
+
+- (void)slk_dismissTextInputbarIfNeeded
+{
+ if (self.keyboardHC.constant == 0) {
+ return;
+ }
+
+ self.keyboardHC.constant = 0.0;
+ self.scrollViewHC.constant = [self slk_appropriateScrollViewHeight];
+
+ [self slk_hideAutoCompletionViewIfNeeded];
+
+ // Forces the keyboard status change
+ [self slk_updateKeyboardStatus:SLKKeyboardStatusDidHide];
+
+ [self.view layoutIfNeeded];
+}
+
+- (BOOL)slk_detectExternalKeyboardInNotification:(NSNotification *)notification
+{
+ if (!self.isMovingKeyboard) {
+ // Based on http://stackoverflow.com/a/5760910/287403
+ // We can determine if the external keyboard is showing by adding the origin.y of the target finish rect (end when showing, begin when hiding) to the inputAccessoryHeight.
+ // If it's greater(or equal) the window height, it's an external keyboard.
+ CGFloat inputAccessoryHeight = self.textView.inputAccessoryView.frame.size.height;
+ CGRect beginRect = [notification.userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue];
+ CGRect endRect = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
+
+ // Grab the base view for conversions as we don't want window coordinates in < iOS 8
+ // iOS 8 fixes the whole coordinate system issue for us, but iOS 7 doesn't rotate the app window coordinate space.
+ UIView *baseView = ((UIWindow *)self.view.window).rootViewController.view;
+
+ // Convert the main screen bounds into the correct coordinate space but ignore the origin.
+ CGRect bounds = [self.view convertRect:[UIScreen mainScreen].bounds fromView:nil];
+ bounds = CGRectMake(0, 0, bounds.size.width, bounds.size.height);
+
+ // We want these rects in the correct coordinate space as well.
+ CGRect convertBegin = [baseView convertRect:beginRect fromView:nil];
+ CGRect convertEnd = [baseView convertRect:endRect fromView:nil];
+
+ if ([notification.name isEqualToString:UIKeyboardWillShowNotification]) {
+ if (convertEnd.origin.y + inputAccessoryHeight >= bounds.size.height) {
+ return YES;
+ }
+ }
+ else if ([notification.name isEqualToString:UIKeyboardWillHideNotification]) {
+ // The additional logic check here (== to width) accounts for a glitch (iOS 8 only?) where the window has rotated it's coordinates
+ // but the beginRect doesn't yet reflect that. It should never cause a false positive.
+ if (convertBegin.origin.y + inputAccessoryHeight >= bounds.size.height ||
+ convertBegin.origin.y + inputAccessoryHeight == bounds.size.width) {
+ return YES;
+ }
+ }
+ }
+ return NO;
+}
+
+- (void)slk_reloadInputAccessoryViewIfNeeded
+{
+ // Reload only if the input views if the text view is first responder.
+ if (!self.isKeyboardPanningEnabled || ![self.textView isFirstResponder]) {
+
+ // Disables the input accessory when not first responder so when showing the keyboard back, there is no delay in the animation.
+ if (self.textView.inputAccessoryView) {
+ self.textView.inputAccessoryView = nil;
+ [self.textView refreshInputViews];
+ }
+ }
+ // Reload only if the input views if the frame doesn't match the text input bar's.
+ else if (CGRectGetHeight(self.textView.inputAccessoryView.frame) != CGRectGetHeight(self.textInputbar.bounds)) {
+ self.textView.inputAccessoryView = [self emptyInputAccessoryView];
+ [self.textView refreshInputViews];
+ }
+}
+
+- (void)slk_adjustContentConfigurationIfNeeded
+{
+ // When inverted, we need to substract the top bars height (generally status bar + navigation bar's) to align the top of the
+ // scrollView correctly to its top edge.
+ if (self.inverted) {
+ UIEdgeInsets contentInset = self.scrollViewProxy.contentInset;
+ contentInset.bottom = [self slk_topBarsHeight];
+
+ self.scrollViewProxy.contentInset = contentInset;
+ self.scrollViewProxy.scrollIndicatorInsets = contentInset;
+ }
+
+ // Substracts the bottom edge rect if present. This fixes the text input layout when using inside of a view controller container
+ // such as a UITabBarController or a custom container.
+ if (((self.edgesForExtendedLayout & UIRectEdgeBottom) > 0)) {
+ self.edgesForExtendedLayout = self.edgesForExtendedLayout & ~UIRectEdgeBottom;
+ }
+}
+
+- (void)slk_prepareForInterfaceRotation
+{
+ [self.view layoutIfNeeded];
+
+ if ([self.textView isFirstResponder]) {
+ [self.textView slk_scrollToCaretPositonAnimated:NO];
+ }
+ else {
+ [self.textView slk_scrollToBottomAnimated:NO];
+ }
+}
+
+
+#pragma mark - Keyboard Events
+
+- (void)didPressReturnKey:(id)sender
+{
+ if (self.textInputbar.isEditing) {
+ [self didCommitTextEditing:sender];
+ return;
+ }
+
+ [self slk_performRightAction];
+}
+
+- (void)didPressEscapeKey:(id)sender
+{
+ if (self.isAutoCompleting) {
+ [self cancelAutoCompletion];
+ }
+ else if (self.textInputbar.isEditing) {
+ [self didCancelTextEditing:sender];
+ }
+
+ if (self.isExternalKeyboardDetected || ([self.textView isFirstResponder] && self.keyboardHC.constant == 0)) {
+ return;
+ }
+
+ [self dismissKeyboard:YES];
+}
+
+- (void)didPressArrowKey:(id)sender
+{
+ [self.textView didPressAnyArrowKey:sender];
+}
+
+
+#pragma mark - Notification Events
+
+- (void)slk_willShowOrHideKeyboard:(NSNotification *)notification
+{
+ // Skips if the view isn't visible.
+ if (!self.view.window) {
+ return;
+ }
+
+ // Skips if it is presented inside of a popover.
+ if (self.isPresentedInPopover) {
+ return;
+ }
+
+ // Skips if textview did refresh only.
+ if (self.textView.didNotResignFirstResponder) {
+ return;
+ }
+
+ // Skips this it's not the expected textView and shouldn't force adjustment of the text input bar.
+ // This will also dismiss the text input bar if it's visible, and exit auto-completion mode if enabled.
+ if (![self.textView isFirstResponder] && ![self forceTextInputbarAdjustmentForResponder:[UIResponder slk_currentFirstResponder]]) {
+ return [self slk_dismissTextInputbarIfNeeded];
+ }
+
+ NSInteger curve = [notification.userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue];
+ NSTimeInterval duration = [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
+
+ SLKKeyboardStatus status = [self slk_keyboardStatusForNotification:notification];
+
+ // Programatically stops scrolling before updating the view constraints (to avoid scrolling glitch).
+ if (status == SLKKeyboardStatusWillShow) {
+ [self.scrollViewProxy slk_stopScrolling];
+ }
+
+ // Hides the auto-completion view if the keyboard is being dismissed.
+ if (![self.textView isFirstResponder] || status == SLKKeyboardStatusWillHide) {
+ [self slk_hideAutoCompletionViewIfNeeded];
+ }
+
+ // Updates the height constraints' constants
+ self.keyboardHC.constant = [self slk_appropriateKeyboardHeight:notification];
+ self.scrollViewHC.constant = [self slk_appropriateScrollViewHeight];
+
+ // Updates and notifies about the keyboard status update
+ if ([self slk_updateKeyboardStatus:status]) {
+ // Posts custom keyboard notification, if logical conditions apply
+ [self slk_postKeyboarStatusNotification:notification];
+ }
+
+ // Only for this animation, we set bo to bounce since we want to give the impression that the text input is glued to the keyboard.
+ [self.view slk_animateLayoutIfNeededWithDuration:duration
+ bounce:NO
+ options:(curve<<16)|UIViewAnimationOptionLayoutSubviews|UIViewAnimationOptionBeginFromCurrentState
+ animations:^{
+ [self slk_scrollToBottomIfNeeded];
+ }];
+}
+
+- (void)slk_didShowOrHideKeyboard:(NSNotification *)notification
+{
+ // Skips if the view isn't visible
+ if (!self.view.window) {
+ return;
+ }
+
+ // Skips if it is presented inside of a popover
+ if (self.isPresentedInPopover) {
+ return;
+ }
+
+ // Skips if textview did refresh only
+ if (self.textView.didNotResignFirstResponder) {
+ return;
+ }
+
+ SLKKeyboardStatus status = [self slk_keyboardStatusForNotification:notification];
+
+ // Skips if it's the current status
+ if (self.keyboardStatus == status) {
+ return;
+ }
+
+ // After showing keyboard, check if the current cursor position could diplay autocompletion
+ if ([self.textView isFirstResponder] && status == SLKKeyboardStatusDidShow && !self.isAutoCompleting) {
+
+ // Wait till the end of the current run loop
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [self slk_processTextForAutoCompletion];
+ });
+ }
+
+ // Updates and notifies about the keyboard status update
+ if ([self slk_updateKeyboardStatus:status]) {
+ // Posts custom keyboard notification, if logical conditions apply
+ [self slk_postKeyboarStatusNotification:notification];
+ }
+
+ // Updates the dismiss mode and input accessory view, if needed.
+ [self slk_reloadInputAccessoryViewIfNeeded];
+
+ // Very important to invalidate this flag after the keyboard is dismissed or presented, to start with a clean state next time.
+ self.movingKeyboard = NO;
+}
+
+- (void)slk_didChangeKeyboardFrame:(NSNotification *)notification
+{
+ // Skips if the view isn't visible
+ if (!self.view.window) {
+ return;
+ }
+
+ // Skips if it is presented inside of a popover
+ if (self.isPresentedInPopover) {
+ return;
+ }
+
+ // Skips this if it's not the expected textView.
+ // Checking the keyboard height constant helps to disable the view constraints update on iPad when the keyboard is undocked.
+ // Checking the keyboard status allows to keep the inputAccessoryView valid when still reacing the bottom of the screen.
+ if (![self.textView isFirstResponder] || (self.keyboardHC.constant == 0 && self.keyboardStatus == SLKKeyboardStatusDidHide)) {
+ return;
+ }
+
+ if (self.scrollViewProxy.isDragging) {
+ self.movingKeyboard = YES;
+ }
+
+ if (self.isMovingKeyboard == NO) {
+ return;
+ }
+
+ self.keyboardHC.constant = [self slk_appropriateKeyboardHeight:notification];
+ self.scrollViewHC.constant = [self slk_appropriateScrollViewHeight];
+
+ // layoutIfNeeded must be called before any further scrollView internal adjustments (content offset and size)
+ [self.view layoutIfNeeded];
+
+ // Overrides the scrollView's contentOffset to allow following the same position when dragging the keyboard
+ CGPoint offset = _scrollViewOffsetBeforeDragging;
+
+ if (self.isInverted) {
+ if (!self.scrollViewProxy.isDecelerating && self.scrollViewProxy.isTracking) {
+ self.scrollViewProxy.contentOffset = _scrollViewOffsetBeforeDragging;
+ }
+ }
+ else {
+ CGFloat keyboardHeightDelta = _keyboardHeightBeforeDragging-self.keyboardHC.constant;
+ offset.y -= keyboardHeightDelta;
+
+ self.scrollViewProxy.contentOffset = offset;
+ }
+}
+
+- (void)slk_didPostSLKKeyboardNotification:(NSNotification *)notification
+{
+ if (![notification.object isEqual:self.textView]) {
+ return;
+ }
+
+ // Used for debug only
+ NSLog(@"%@ %s: %@", NSStringFromClass([self class]), __FUNCTION__, notification);
+}
+
+- (void)slk_willChangeTextViewText:(NSNotification *)notification
+{
+ // Skips this it's not the expected textView.
+ if (![notification.object isEqual:self.textView]) {
+ return;
+ }
+
+ [self textWillUpdate];
+}
+
+- (void)slk_didChangeTextViewText:(NSNotification *)notification
+{
+ // Skips this it's not the expected textView.
+ if (![notification.object isEqual:self.textView]) {
+ return;
+ }
+
+ // Animated only if the view already appeared.
+ [self textDidUpdate:self.isViewVisible];
+
+ // Process the text at every change, when the view is visible
+ if (self.isViewVisible) {
+ [self slk_processTextForAutoCompletion];
+ }
+}
+
+- (void)slk_didChangeTextViewContentSize:(NSNotification *)notification
+{
+ // Skips this it's not the expected textView.
+ if (![notification.object isEqual:self.textView]) {
+ return;
+ }
+
+ // Animated only if the view already appeared.
+ [self textDidUpdate:self.isViewVisible];
+}
+
+- (void)slk_didChangeTextViewSelectedRange:(NSNotification *)notification
+{
+ // Skips this it's not the expected textView.
+ if (![notification.object isEqual:self.textView]) {
+ return;
+ }
+
+ [self textSelectionDidChange];
+}
+
+- (void)slk_didChangeTextViewPasteboard:(NSNotification *)notification
+{
+ // Skips this if it's not the expected textView.
+ if (![self.textView isFirstResponder]) {
+ return;
+ }
+
+ // Notifies only if the pasted item is nested in a dictionary.
+ if ([notification.userInfo isKindOfClass:[NSDictionary class]]) {
+ [self didPasteMediaContent:notification.userInfo];
+ }
+}
+
+- (void)slk_didShakeTextView:(NSNotification *)notification
+{
+ // Skips this if it's not the expected textView.
+ if (![self.textView isFirstResponder]) {
+ return;
+ }
+
+ // Notifies of the shake gesture if undo mode is on and the text view is not empty
+ if (self.shakeToClearEnabled && self.textView.text.length > 0) {
+ [self willRequestUndo];
+ }
+}
+
+- (void)slk_willShowOrHideTypeIndicatorView:(NSNotification *)notification
+{
+ SLKTypingIndicatorView *indicatorView = (SLKTypingIndicatorView *)notification.object;
+
+ // Skips if it's not the expected typing indicator view.
+ if (![indicatorView isEqual:self.typingIndicatorView]) {
+ return;
+ }
+
+ // Skips if the typing indicator should not show. Ignores the checking if it's trying to hide.
+ if (![self canShowTypeIndicator] && !self.typingIndicatorView.isVisible) {
+ return;
+ }
+
+ self.typingIndicatorViewHC.constant = indicatorView.isVisible ? 0.0 : indicatorView.intrinsicContentSize.height;
+ self.scrollViewHC.constant -= self.typingIndicatorViewHC.constant;
+
+ [self.view slk_animateLayoutIfNeededWithBounce:self.bounces
+ options:UIViewAnimationOptionCurveEaseInOut|UIViewAnimationOptionLayoutSubviews|UIViewAnimationOptionBeginFromCurrentState
+ animations:NULL];
+}
+
+- (void)slk_willTerminateApplication:(NSNotification *)notification
+{
+ // Caches the text before it's too late!
+ [self slk_cacheTextView];
+}
+
+
+#pragma mark - Auto-Completion Text Processing
+
+- (void)registerPrefixesForAutoCompletion:(NSArray *)prefixes
+{
+ NSMutableArray *array = [NSMutableArray arrayWithArray:self.registeredPrefixes];
+
+ for (NSString *prefix in prefixes) {
+ // Skips if the prefix is not a valid string
+ if (![prefix isKindOfClass:[NSString class]] || prefix.length == 0) {
+ continue;
+ }
+
+ // Adds the prefix if not contained already
+ if (![array containsObject:prefix]) {
+ [array addObject:prefix];
+ }
+ }
+
+ if (_registeredPrefixes) {
+ _registeredPrefixes = nil;
+ }
+
+ _registeredPrefixes = [[NSArray alloc] initWithArray:array];
+}
+
+- (void)slk_processTextForAutoCompletion
+{
+ if (self.isRotating) {
+ return;
+ }
+
+ // Avoids text processing for auto-completion if the registered prefix list is empty.
+ if (self.registeredPrefixes.count == 0) {
+ return;
+ }
+
+ NSString *text = self.textView.text;
+
+ // Skip, when there is no text to process
+ if (text.length == 0) {
+ return [self cancelAutoCompletion];
+ }
+
+ NSRange range;
+ NSString *word = [self.textView slk_wordAtCaretRange:&range];
+
+ [self slk_invalidateAutoCompletion];
+
+ if (word.length > 0) {
+ NSString *prefix = [word substringWithRange:NSMakeRange(0, 1)];
+
+ if ([self.registeredPrefixes containsObject:prefix]) {
+ // Captures the detected symbol prefix
+ _foundPrefix = prefix;
+
+ // Used later for replacing the detected range with a new string alias returned in -acceptAutoCompletionWithString:
+ _foundPrefixRange = NSMakeRange(range.location, prefix.length);
+ }
+ }
+
+ [self slk_handleProcessedWord:word range:range];
+}
+
+- (void)slk_handleProcessedWord:(NSString *)word range:(NSRange)range
+{
+ // Cancel auto-completion if the cursor is placed before the prefix
+ if (self.textView.selectedRange.location <= self.foundPrefixRange.location) {
+ return [self cancelAutoCompletion];
+ }
+
+ if (self.foundPrefix.length > 0) {
+ if (range.length == 0 || range.length != word.length) {
+ return [self cancelAutoCompletion];
+ }
+
+ if (word.length > 0) {
+ // Removes the found prefix
+ _foundWord = [word substringFromIndex:self.foundPrefix.length];
+
+ // If the prefix is still contained in the word, cancels
+ if ([self.foundWord rangeOfString:self.foundPrefix].location != NSNotFound) {
+ return [self cancelAutoCompletion];
+ }
+ }
+ else {
+ return [self cancelAutoCompletion];
+ }
+ }
+ else {
+ return [self cancelAutoCompletion];
+ }
+
+ [self slk_showAutoCompletionView:[self canShowAutoCompletion]];
+}
+
+- (void)cancelAutoCompletion
+{
+ [self slk_invalidateAutoCompletion];
+ [self slk_hideAutoCompletionViewIfNeeded];
+}
+
+- (void)slk_invalidateAutoCompletion
+{
+ _foundPrefix = nil;
+ _foundWord = nil;
+ _foundPrefixRange = NSMakeRange(0, 0);
+
+ [self.autoCompletionView setContentOffset:CGPointZero];
+}
+
+- (void)acceptAutoCompletionWithString:(NSString *)string
+{
+ [self acceptAutoCompletionWithString:string keepPrefix:YES];
+}
+
+- (void)acceptAutoCompletionWithString:(NSString *)string keepPrefix:(BOOL)keepPrefix
+{
+ if (string.length == 0) {
+ return;
+ }
+
+ SLKTextView *textView = self.textView;
+
+ NSUInteger location = self.foundPrefixRange.location;
+ if (keepPrefix) {
+ location += self.foundPrefixRange.length;
+ }
+
+ NSUInteger length = self.foundWord.length;
+ if (!keepPrefix) {
+ length += self.foundPrefixRange.length;
+ }
+
+ NSRange range = NSMakeRange(location, length);
+ NSRange insertionRange = [textView slk_insertText:string inRange:range];
+
+ textView.selectedRange = NSMakeRange(insertionRange.location, 0);
+
+ [self cancelAutoCompletion];
+
+ [textView slk_scrollToCaretPositonAnimated:NO];
+}
+
+- (void)slk_hideAutoCompletionViewIfNeeded
+{
+ if (self.isAutoCompleting) {
+ [self slk_showAutoCompletionView:NO];
+ }
+}
+
+- (void)slk_showAutoCompletionView:(BOOL)show
+{
+ // Skips if rotating
+ if (self.isRotating) {
+ return;
+ }
+
+ // Reloads the tableview before showing/hiding
+ [self.autoCompletionView reloadData];
+
+ self.autoCompleting = show;
+
+ // Toggles auto-correction if requiered
+ [self slk_enableTypingSuggestionIfNeeded];
+
+ CGFloat viewHeight = show ? [self heightForAutoCompletionView] : 0.0;
+
+ if (self.autoCompletionViewHC.constant == viewHeight) {
+ return;
+ }
+
+ // If the auto-completion view height is bigger than the maximum height allows, it is reduce to that size. Default 140 pts.
+ CGFloat maximumHeight = [self maximumHeightForAutoCompletionView];
+
+ if (viewHeight > maximumHeight) {
+ viewHeight = maximumHeight;
+ }
+
+ CGFloat tableHeight = self.scrollViewHC.constant + self.autoCompletionViewHC.constant;
+
+ // On iPhone, the auto-completion view can't extend beyond the table view height
+ if (SLK_IS_IPHONE && viewHeight > tableHeight) {
+ viewHeight = tableHeight;
+ }
+
+ self.autoCompletionViewHC.constant = viewHeight;
+
+ [self.view slk_animateLayoutIfNeededWithBounce:self.bounces
+ options:UIViewAnimationOptionCurveEaseInOut|UIViewAnimationOptionLayoutSubviews|UIViewAnimationOptionBeginFromCurrentState
+ animations:NULL];
+}
+
+
+#pragma mark - Text Caching
+
+- (NSString *)keyForTextCaching
+{
+ // No implementation here. Meant to be overriden in subclass.
+ return nil;
+}
+
+- (NSString *)slk_keyForPersistency
+{
+ NSString *keyForTextCaching = [self keyForTextCaching];
+ NSString *previousCachedText = [[NSUserDefaults standardUserDefaults] objectForKey:keyForTextCaching];
+
+ if ([previousCachedText isKindOfClass:[NSString class]]) {
+ return keyForTextCaching;
+ }
+ else {
+ return [NSString stringWithFormat:@"%@.%@", SLKTextViewControllerDomain, [self keyForTextCaching]];
+ }
+}
+
+- (void)slk_reloadTextView
+{
+ if (self.textView.text.length > 0 || !self.slk_isCachingEnabled) {
+ return;
+ }
+
+ self.textView.text = [self slk_cachedText];
+}
+
+- (void)slk_cacheTextView
+{
+ [self slk_cacheTextToDisk:self.textView.text];
+}
+
+- (void)clearCachedText
+{
+ [self slk_cacheTextToDisk:nil];
+}
+
++ (void)clearAllCachedText
+{
+ NSMutableArray *cachedKeys = [NSMutableArray new];
+
+ for (NSString *key in [[[NSUserDefaults standardUserDefaults] dictionaryRepresentation] allKeys]) {
+ if ([key rangeOfString:SLKTextViewControllerDomain].location != NSNotFound) {
+ [cachedKeys addObject:key];
+ }
+ }
+
+ if (cachedKeys.count == 0) {
+ return;
+ }
+
+ for (NSString *cachedKey in cachedKeys) {
+ [[NSUserDefaults standardUserDefaults] removeObjectForKey:cachedKey];
+ }
+
+ [[NSUserDefaults standardUserDefaults] synchronize];
+}
+
+- (BOOL)slk_isCachingEnabled
+{
+ return ([self keyForTextCaching] != nil);
+}
+
+- (NSString *)slk_cachedText
+{
+ if (!self.slk_isCachingEnabled) {
+ return nil;
+ }
+
+ NSString *key = [self slk_keyForPersistency];
+ return [[NSUserDefaults standardUserDefaults] objectForKey:key];
+}
+
+- (void)slk_cacheTextToDisk:(NSString *)text
+{
+ if (!self.slk_isCachingEnabled) {
+ return;
+ }
+
+ NSString *cachedText = [self slk_cachedText];
+ NSString *key = [self slk_keyForPersistency];
+
+ // Caches text only if its a valid string and not already cached
+ if (text.length > 0 && ![text isEqualToString:cachedText]) {
+ [[NSUserDefaults standardUserDefaults] setObject:text forKey:key];
+ }
+ // Clears cache only if it exists
+ else if (text.length == 0 && cachedText.length > 0) {
+ [[NSUserDefaults standardUserDefaults] removeObjectForKey:key];
+ }
+ else {
+ // Skips so it doesn't hit 'synchronize' unnecessarily
+ return;
+ }
+
+ [[NSUserDefaults standardUserDefaults] synchronize];
+}
+
+
+#pragma mark - Customization
+
+- (void)registerClassForTextView:(Class)textViewClass
+{
+ if (textViewClass == nil) {
+ return;
+ }
+
+ NSAssert([textViewClass isSubclassOfClass:[SLKTextView class]], @"The registered class is invalid, it must be a subclass of SLKTextView.");
+ self.textViewClass = textViewClass;
+}
+
+
+#pragma mark - UITextViewDelegate Methods
+
+- (BOOL)textView:(SLKTextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
+{
+ self.newWordInserted = ([text rangeOfCharacterFromSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]].location != NSNotFound);
+
+ // It should not change if auto-completion is active and trying to replace with an auto-correction suggested text.
+ if (self.isAutoCompleting && text.length > 1) {
+ return NO;
+ }
+
+ // Records text for undo for every new word
+ if (self.newWordInserted) {
+ [self.textView slk_prepareForUndo:@"Word Change"];
+ }
+
+ if ([text isEqualToString:@"\n"]) {
+ //Detected break. Should insert new line break manually.
+ [textView slk_insertNewLineBreak];
+
+ return NO;
+ }
+ else {
+ NSDictionary *userInfo = @{@"text": text, @"range": [NSValue valueWithRange:range]};
+ [[NSNotificationCenter defaultCenter] postNotificationName:SLKTextViewTextWillChangeNotification object:self.textView userInfo:userInfo];
+
+ return YES;
+ }
+}
+
+- (void)textViewDidChange:(UITextView *)textView
+{
+ // Keep to avoid unnecessary crashes. Was meant to be overriden in subclass while calling super.
+}
+
+- (void)textViewDidChangeSelection:(UITextView *)textView
+{
+ // Keep to avoid unnecessary crashes. Was meant to be overriden in subclass while calling super.
+}
+
+
+#pragma mark - UITableViewDataSource Methods
+
+- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
+{
+ return 0;
+}
+
+- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
+{
+ return nil;
+}
+
+
+#pragma mark - UICollectionViewDataSource Methods
+
+- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section;
+{
+ return 0;
+}
+
+- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
+{
+ return nil;
+}
+
+
+#pragma mark - UIScrollViewDelegate Methods
+
+- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView
+{
+ return [self slk_scrollToTopIfNeeded];
+}
+
+- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
+{
+ self.movingKeyboard = NO;
+}
+
+- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
+{
+ self.movingKeyboard = NO;
+}
+
+- (void)scrollViewDidScroll:(UIScrollView *)scrollView
+{
+ if ([scrollView isEqual:self.autoCompletionView]) {
+ CGRect frame = self.autoCompletionHairline.frame;
+ frame.origin.y = scrollView.contentOffset.y;
+ self.autoCompletionHairline.frame = frame;
+ }
+ else {
+ if (!self.isMovingKeyboard) {
+ _scrollViewOffsetBeforeDragging = scrollView.contentOffset;
+ _keyboardHeightBeforeDragging = self.keyboardHC.constant;
+ }
+ }
+}
+
+
+#pragma mark - UIGestureRecognizerDelegate Methods
+
+- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gesture
+{
+ if ([gesture isEqual:self.singleTapGesture]) {
+ return [self.textView isFirstResponder] && !self.isExternalKeyboardDetected;
+ }
+ else if ([gesture isEqual:self.verticalPanGesture]) {
+
+ if ([self.textView isFirstResponder]) {
+ return NO;
+ }
+
+ CGPoint velocity = [self.verticalPanGesture velocityInView:self.view];
+
+ // Vertical panning, from bottom to top only
+ if (velocity.y < 0 && ABS(velocity.y) > ABS(velocity.x) && ![self.textInputbar.textView isFirstResponder]) {
+ return YES;
+ }
+ }
+
+ return NO;
+}
+
+
+#pragma mark - UIAlertViewDelegate Methods
+
+- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
+{
+ if (alertView.tag == SLKAlertViewClearTextTag && self.shakeToClearEnabled && buttonIndex != [alertView cancelButtonIndex] ) {
+ // Clears the text but doesn't clear the undo manager
+ [self.textView slk_clearText:NO];
+ }
+}
+
+
+#pragma mark - View Auto-Layout
+
+- (void)slk_setupViewConstraints
+{
+ NSDictionary *views = @{@"scrollView": self.scrollViewProxy,
+ @"autoCompletionView": self.autoCompletionView,
+ @"typingIndicatorView": self.typingIndicatorView,
+ @"textInputbar": self.textInputbar,
+ };
+
+ [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[scrollView(0@750)][autoCompletionView(0@750)][typingIndicatorView(0)]-0@999-[textInputbar(>=0)]|" options:0 metrics:nil views:views]];
+ [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView]|" options:0 metrics:nil views:views]];
+ [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[autoCompletionView]|" options:0 metrics:nil views:views]];
+ [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[typingIndicatorView]|" options:0 metrics:nil views:views]];
+ [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[textInputbar]|" options:0 metrics:nil views:views]];
+
+ self.scrollViewHC = [self.view slk_constraintForAttribute:NSLayoutAttributeHeight firstItem:self.scrollViewProxy secondItem:nil];
+ self.autoCompletionViewHC = [self.view slk_constraintForAttribute:NSLayoutAttributeHeight firstItem:self.autoCompletionView secondItem:nil];
+ self.typingIndicatorViewHC = [self.view slk_constraintForAttribute:NSLayoutAttributeHeight firstItem:self.typingIndicatorView secondItem:nil];
+ self.textInputbarHC = [self.view slk_constraintForAttribute:NSLayoutAttributeHeight firstItem:self.textInputbar secondItem:nil];
+ self.keyboardHC = [self.view slk_constraintForAttribute:NSLayoutAttributeBottom firstItem:self.view secondItem:self.textInputbar];
+
+ self.textInputbarHC.constant = self.textInputbar.minimumInputbarHeight;
+ self.scrollViewHC.constant = [self slk_appropriateScrollViewHeight];
+
+ if (self.textInputbar.isEditing) {
+ self.textInputbarHC.constant += self.textInputbar.editorContentViewHeight;
+ }
+}
+
+
+#pragma mark - External Keyboard Support
+
+- (NSArray *)keyCommands
+{
+ if (_keyboardCommands) {
+ return _keyboardCommands;
+ }
+
+ _keyboardCommands = @[
+ // Pressing Return key
+ [UIKeyCommand keyCommandWithInput:@"\r" modifierFlags:0 action:@selector(didPressReturnKey:)],
+ // Pressing Esc key
+ [UIKeyCommand keyCommandWithInput:UIKeyInputEscape modifierFlags:0 action:@selector(didPressEscapeKey:)],
+
+ // Arrow keys
+ [UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow modifierFlags:0 action:@selector(didPressArrowKey:)],
+ [UIKeyCommand keyCommandWithInput:UIKeyInputDownArrow modifierFlags:0 action:@selector(didPressArrowKey:)],
+ ];
+
+ return _keyboardCommands;
+}
+
+
+#pragma mark - NSNotificationCenter register/unregister
+
+- (void)slk_registerNotifications
+{
+ // Keyboard notifications
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_willShowOrHideKeyboard:) name:UIKeyboardWillShowNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_willShowOrHideKeyboard:) name:UIKeyboardWillHideNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_didShowOrHideKeyboard:) name:UIKeyboardDidShowNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_didShowOrHideKeyboard:) name:UIKeyboardDidHideNotification object:nil];
+
+#if SLK_KEYBOARD_NOTIFICATION_DEBUG
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_didPostSLKKeyboardNotification:) name:SLKKeyboardWillShowNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_didPostSLKKeyboardNotification:) name:SLKKeyboardDidShowNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_didPostSLKKeyboardNotification:) name:SLKKeyboardWillHideNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_didPostSLKKeyboardNotification:) name:SLKKeyboardDidHideNotification object:nil];
+#endif
+
+ // Keyboard Accessory View notifications
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_didChangeKeyboardFrame:) name:SLKInputAccessoryViewKeyboardFrameDidChangeNotification object:nil];
+
+ // TextView notifications
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_willChangeTextViewText:) name:SLKTextViewTextWillChangeNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_didChangeTextViewText:) name:UITextViewTextDidChangeNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_didChangeTextViewContentSize:) name:SLKTextViewContentSizeDidChangeNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_didChangeTextViewSelectedRange:) name:SLKTextViewSelectedRangeDidChangeNotification object:nil];
+
+
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_didChangeTextViewPasteboard:) name:SLKTextViewDidPasteItemNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_didShakeTextView:) name:SLKTextViewDidShakeNotification object:nil];
+
+ // TypeIndicator notifications
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_willShowOrHideTypeIndicatorView:) name:SLKTypingIndicatorViewWillShowNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_willShowOrHideTypeIndicatorView:) name:SLKTypingIndicatorViewWillHideNotification object:nil];
+
+ // Application notifications
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_willTerminateApplication:) name:UIApplicationWillTerminateNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_willTerminateApplication:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
+}
+
+- (void)slk_unregisterNotifications
+{
+ // Keyboard notifications
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidShowNotification object:nil];
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidHideNotification object:nil];
+
+#if SLK_KEYBOARD_NOTIFICATION_DEBUG
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:SLKKeyboardWillShowNotification object:nil];
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:SLKKeyboardDidShowNotification object:nil];
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:SLKKeyboardWillHideNotification object:nil];
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:SLKKeyboardDidHideNotification object:nil];
+#endif
+
+ // TextView notifications
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:SLKInputAccessoryViewKeyboardFrameDidChangeNotification object:nil];
+
+ // TextView notifications
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:UITextViewTextDidBeginEditingNotification object:nil];
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:UITextViewTextDidEndEditingNotification object:nil];
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:SLKTextViewTextWillChangeNotification object:nil];
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:UITextViewTextDidChangeNotification object:nil];
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:SLKTextViewContentSizeDidChangeNotification object:nil];
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:SLKTextViewSelectedRangeDidChangeNotification object:nil];
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:SLKTextViewDidPasteItemNotification object:nil];
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:SLKTextViewDidShakeNotification object:nil];
+
+ // TypeIndicator notifications
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:SLKTypingIndicatorViewWillShowNotification object:nil];
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:SLKTypingIndicatorViewWillHideNotification object:nil];
+
+ // Application notifications
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillTerminateNotification object:nil];
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
+}
+
+
+#pragma mark - View Auto-Rotation
+
+- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
+{
+ self.rotating = YES;
+
+ [self slk_prepareForInterfaceRotation];
+}
+
+- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
+{
+ // Delays the rotation flag
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+ self.rotating = NO;
+ });
+}
+
+- (NSUInteger)supportedInterfaceOrientations
+{
+ return UIInterfaceOrientationMaskAll;
+}
+
+- (BOOL)shouldAutorotate
+{
+ return YES;
+}
+
+
+#pragma mark - View lifeterm
+
+- (void)didReceiveMemoryWarning
+{
+ [super didReceiveMemoryWarning];
+}
+
+- (void)dealloc
+{
+ _tableView.delegate = nil;
+ _tableView.dataSource = nil;
+ _tableView = nil;
+
+ _collectionView.delegate = nil;
+ _collectionView.dataSource = nil;
+ _collectionView = nil;
+
+ _scrollView = nil;
+
+ _autoCompletionView.delegate = nil;
+ _autoCompletionView.dataSource = nil;
+ _autoCompletionView = nil;
+
+ _textInputbar.textView.delegate = nil;
+ _textInputbar = nil;
+ _typingIndicatorView = nil;
+
+ _registeredPrefixes = nil;
+ _keyboardCommands = nil;
+
+ _singleTapGesture.delegate = nil;
+ _singleTapGesture = nil;
+ _verticalPanGesture.delegate = nil;
+ _verticalPanGesture = nil;
+ _scrollViewHC = nil;
+ _textInputbarHC = nil;
+ _textInputbarHC = nil;
+ _typingIndicatorViewHC = nil;
+ _autoCompletionViewHC = nil;
+ _keyboardHC = nil;
+
+ [self slk_unregisterNotifications];
+}
+
+@end
diff --git a/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Classes/SLKTypingIndicatorView.h b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Classes/SLKTypingIndicatorView.h
new file mode 100644
index 000000000..31f648199
--- /dev/null
+++ b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Classes/SLKTypingIndicatorView.h
@@ -0,0 +1,78 @@
+//
+// Copyright 2014 Slack Technologies, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import
+
+UIKIT_EXTERN NSString * const SLKTypingIndicatorViewWillShowNotification;
+UIKIT_EXTERN NSString * const SLKTypingIndicatorViewWillHideNotification;
+
+/** @name A custom view to display an indicator of users typing. */
+@interface SLKTypingIndicatorView : UIView
+
+/** The amount of time a name should keep visible. If is zero, the indicator will not remove nor disappear automatically. Default is 6.0 seconds*/
+@property (nonatomic, readwrite) NSTimeInterval interval;
+
+/** If YES, the user can dismiss the indicator by tapping on it. Default is YES. */
+@property (nonatomic, readwrite) BOOL canResignByTouch;
+
+/** Returns YES if the indicator is visible. */
+@property (nonatomic, readwrite, getter = isVisible) BOOL visible;
+
+/** The appropriate height of the view. */
+@property (nonatomic, readonly) CGFloat height;
+
+/** The color of the text. Default is grayColor. */
+@property (nonatomic, strong) UIColor *textColor;
+
+/** The font of the text. Default is system font, 12 pts. */
+@property (nonatomic, strong) UIFont *textFont;
+
+/** The font to be used when matching a username string. Default is system bold font, 12 pts. */
+@property (nonatomic, strong) UIFont *highlightFont;
+
+/** The inner padding to use when laying out content in the view. Default is {10, 40, 10, 10}. */
+@property (nonatomic, assign) UIEdgeInsets contentInset;
+
+/**
+ Inserts a user name, only if that user name is not yet on the list.
+ Each inserted name has an attached timer, which will automatically remove the name from the list once the interval is reached (default 6 seconds).
+
+ The control follows a set of display rules, to accomodate the screen size:
+
+ - When only 1 user name is set, it will display ":name is typing"
+
+ - When only 2 user names are set, it will display ":name & :name are typing"
+
+ - When more than 2 user names are set, it will display "several people are typing"
+
+ @param username The user name string.
+ */
+- (void)insertUsername:(NSString *)username;
+
+/**
+ Removes a user name, if existent on the list.
+ Once there are no more items on the list, the indicator will automatically try to hide (by setting it self to visible = NO).
+
+ @param username The user name string.
+ */
+- (void)removeUsername:(NSString *)username;
+
+/**
+ Dismisses the indicator view.
+ */
+- (void)dismissIndicator;
+
+@end
diff --git a/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Classes/SLKTypingIndicatorView.m b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Classes/SLKTypingIndicatorView.m
new file mode 100644
index 000000000..fc299173c
--- /dev/null
+++ b/src/ui/ios/GF-Offline-Translator/Pods/SlackTextViewController/Source/Classes/SLKTypingIndicatorView.m
@@ -0,0 +1,358 @@
+//
+// Copyright 2014 Slack Technologies, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import "SLKTypingIndicatorView.h"
+#import "UIView+SLKAdditions.h"
+#import "SLKUIConstants.h"
+
+NSString * const SLKTypingIndicatorViewWillShowNotification = @"SLKTypingIndicatorViewWillShowNotification";
+NSString * const SLKTypingIndicatorViewWillHideNotification = @"SLKTypingIndicatorViewWillHideNotification";
+
+#define SLKTypingIndicatorViewIdentifier [NSString stringWithFormat:@"%@.%@", SLKTextViewControllerDomain, NSStringFromClass([self class])]
+
+@interface SLKTypingIndicatorView ()
+
+// The text label used to display the typing indicator content.
+@property (nonatomic, strong) UILabel *textLabel;
+
+@property (nonatomic, strong) NSMutableArray *usernames;
+@property (nonatomic, strong) NSMutableArray *timers;
+
+// Auto-Layout margin constraints used for updating their constants
+@property (nonatomic, strong) NSLayoutConstraint *leftContraint;
+@property (nonatomic, strong) NSLayoutConstraint *rightContraint;
+
+@end
+
+@implementation SLKTypingIndicatorView
+
+#pragma mark - Initializer
+
+- (id)init
+{
+ if (self = [super init]) {
+ [self slk_commonInit];
+ }
+ return self;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)coder
+{
+ if (self = [super initWithCoder:coder]) {
+ [self slk_commonInit];
+ }
+ return self;
+}
+
+- (void)slk_commonInit
+{
+ self.interval = 6.0;
+ self.canResignByTouch = YES;
+ self.usernames = [NSMutableArray new];
+ self.timers = [NSMutableArray new];
+
+ self.textColor = [UIColor grayColor];
+ self.textFont = [UIFont systemFontOfSize:12.0];
+ self.highlightFont = [UIFont boldSystemFontOfSize:12.0];
+ self.contentInset = UIEdgeInsetsMake(10.0, 40.0, 10.0, 10.0);
+
+ self.backgroundColor = [UIColor whiteColor];
+
+ [self addSubview:self.textLabel];
+
+ [self slk_setupConstraints];
+}
+
+
+#pragma mark - UIView Overrides
+
+- (CGSize)intrinsicContentSize
+{
+ return CGSizeMake(UIViewNoIntrinsicMetric, [self height]);
+}
+
++ (BOOL)requiresConstraintBasedLayout
+{
+ return YES;
+}
+
+
+#pragma mark - Getters
+
+- (UILabel *)textLabel
+{
+ if (!_textLabel)
+ {
+ _textLabel = [UILabel new];
+ _textLabel.translatesAutoresizingMaskIntoConstraints = NO;
+ _textLabel.backgroundColor = [UIColor clearColor];
+ _textLabel.userInteractionEnabled = NO;
+ _textLabel.hidden = YES;
+ }
+ return _textLabel;
+}
+
+- (NSAttributedString *)attributedString
+{
+ if (self.usernames.count == 0) {
+ return nil;
+ }
+
+ NSString *text = nil;
+ NSString *firstObject = [self.usernames firstObject];
+ NSString *lastObject = [self.usernames lastObject];
+
+ if (self.usernames.count == 1) {
+ text = [NSString stringWithFormat:NSLocalizedString(@"%@ is typing", nil), firstObject];
+ }
+ else if (self.usernames.count == 2) {
+ text = [NSString stringWithFormat:NSLocalizedString(@"%@ & %@ are typing", nil), firstObject, lastObject];
+ }
+ else if (self.usernames.count > 2) {
+ text = NSLocalizedString(@"Several people are typing", nil);
+ }
+
+ NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
+ style.alignment = NSTextAlignmentLeft;
+ style.lineBreakMode = NSLineBreakByTruncatingTail;
+ style.minimumLineHeight = 10.0;
+
+ NSDictionary *attributes = @{NSFontAttributeName: self.textFont,
+ NSForegroundColorAttributeName: self.textColor,
+ NSParagraphStyleAttributeName: style,
+ };
+
+ NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:text attributes:attributes];
+
+ if (self.usernames.count <= 2) {
+ [attributedString addAttribute:NSFontAttributeName value:self.highlightFont range:[text rangeOfString:firstObject]];
+ [attributedString addAttribute:NSFontAttributeName value:self.highlightFont range:[text rangeOfString:lastObject]];
+ }
+
+ return attributedString;
+}
+
+- (CGFloat)height
+{
+ CGFloat height = self.textFont.lineHeight;
+ height += self.contentInset.top;
+ height += self.contentInset.bottom;
+ return height;
+}
+
+
+- (NSTimer *)slk_timerWithIdentifier:(NSString *)identifier
+{
+ for (NSTimer *timer in self.timers) {
+ if ([identifier isEqualToString:[timer.userInfo objectForKey:SLKTypingIndicatorViewIdentifier]]) {
+ return timer;
+ }
+ }
+ return nil;
+}
+
+
+#pragma mark - Setters
+
+- (void)setVisible:(BOOL)visible
+{
+ if (visible == self.visible) {
+ return;
+ }
+
+ NSString *notificationName = visible ? SLKTypingIndicatorViewWillShowNotification : SLKTypingIndicatorViewWillHideNotification;
+ [[NSNotificationCenter defaultCenter] postNotificationName:notificationName object:self];
+
+ if (visible) {
+ self.textLabel.hidden = NO;
+ }
+ else {
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+ self.textLabel.hidden = YES;
+ });
+ }
+
+ _visible = visible;
+
+ if (!visible) {
+ [self slk_cleanAll];
+ }
+}
+
+- (void)setContentInset:(UIEdgeInsets)insets
+{
+ if (UIEdgeInsetsEqualToEdgeInsets(self.contentInset, insets)) {
+ return;
+ }
+
+ if (UIEdgeInsetsEqualToEdgeInsets(self.contentInset, UIEdgeInsetsZero)) {
+ _contentInset = insets;
+ return;
+ }
+
+ _contentInset = insets;
+
+ [self slk_updateConstraintConstants];
+}
+
+
+#pragma mark - Public Methods
+
+- (void)insertUsername:(NSString *)username;
+{
+ if (!username) {
+ return;
+ }
+
+ BOOL isShowing = [self.usernames containsObject:username];
+
+ if (_interval > 0.0) {
+
+ if (isShowing) {
+ NSTimer *timer = [self slk_timerWithIdentifier:username];
+ [self slk_invalidateTimer:timer];
+ }
+
+ NSTimer *timer = [NSTimer timerWithTimeInterval:_interval target:self selector:@selector(slk_shouldRemoveUsername:) userInfo:@{SLKTypingIndicatorViewIdentifier: username} repeats:NO];
+ [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
+ [self.timers addObject:timer];
+ }
+
+ if (isShowing) {
+ return;
+ }
+
+ [self.usernames addObject:username];
+
+ self.textLabel.attributedText = [self attributedString];
+
+ if (!self.isVisible) {
+ [self setVisible:YES];
+ }
+}
+
+- (void)removeUsername:(NSString *)username
+{
+ if (!username || ![self.usernames containsObject:username]) {
+ return;
+ }
+
+ [self.usernames removeObject:username];
+
+ if (self.usernames.count > 0) {
+ self.textLabel.attributedText = [self attributedString];
+ }
+ else if (self.isVisible) {
+ [self setVisible:NO];
+ }
+}
+
+- (void)dismissIndicator
+{
+ if (self.isVisible) {
+ [self setVisible:NO];
+ }
+}
+
+
+#pragma mark - Private Methods
+
+- (void)slk_shouldRemoveUsername:(NSTimer *)timer
+{
+ NSString *identifier = [timer.userInfo objectForKey:SLKTypingIndicatorViewIdentifier];
+
+ [self removeUsername:identifier];
+ [self slk_invalidateTimer:timer];
+}
+
+- (void)slk_invalidateTimer:(NSTimer *)timer
+{
+ if (timer) {
+ [timer invalidate];
+ [self.timers removeObject:timer];
+ timer = nil;
+ }
+}
+
+- (void)slk_invalidateTimers
+{
+ for (NSTimer *timer in self.timers) {
+ [timer invalidate];
+ }
+
+ [self.timers removeAllObjects];
+}
+
+- (void)slk_cleanAll
+{
+ [self slk_invalidateTimers];
+
+ self.textLabel.text = nil;
+
+ [self.usernames removeAllObjects];
+}
+
+
+#pragma mark - View Auto-Layout
+
+- (void)slk_setupConstraints
+{
+ NSDictionary *views = @{@"textLabel": self.textLabel};
+
+ [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[textLabel]|" options:0 metrics:nil views:views]];
+ [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(0)-[textLabel]-(0@750)-|" options:0 metrics:nil views:views]];
+
+ self.leftContraint = [[self slk_constraintsForAttribute:NSLayoutAttributeLeading] firstObject];
+ self.rightContraint = [[self slk_constraintsForAttribute:NSLayoutAttributeTrailing] firstObject];
+
+ [self slk_updateConstraintConstants];
+}
+
+- (void)slk_updateConstraintConstants
+{
+ self.leftContraint.constant = self.contentInset.left;
+ self.rightContraint.constant = self.contentInset.right;
+}
+
+
+#pragma mark - Hit Testing
+
+- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
+{
+ UIView *view = [super hitTest:point withEvent:event];
+
+ if ([view isEqual:self]) {
+ if (self.isVisible && self.canResignByTouch) {
+ [self setVisible:NO];
+ }
+ return view;
+ }
+ return view;
+}
+
+
+#pragma mark - Lifeterm
+
+- (void)dealloc
+{
+ [self slk_cleanAll];
+
+ _textLabel = nil;
+ _usernames = nil;
+ _timers = nil;
+}
+
+@end
\ No newline at end of file
diff --git a/src/ui/ios/GF-Offline-Translator/Pods/Target Support Files/Pods-SlackTextViewController/Pods-SlackTextViewController-Private.xcconfig b/src/ui/ios/GF-Offline-Translator/Pods/Target Support Files/Pods-SlackTextViewController/Pods-SlackTextViewController-Private.xcconfig
new file mode 100644
index 000000000..8682d4301
--- /dev/null
+++ b/src/ui/ios/GF-Offline-Translator/Pods/Target Support Files/Pods-SlackTextViewController/Pods-SlackTextViewController-Private.xcconfig
@@ -0,0 +1,6 @@
+#include "Pods-SlackTextViewController.xcconfig"
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/SlackTextViewController" "${PODS_ROOT}/Headers/Private/SlackTextViewController/Additions" "${PODS_ROOT}/Headers/Private/SlackTextViewController/Classes" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/SlackTextViewController" "${PODS_ROOT}/Headers/Public/SlackTextViewController/Additions" "${PODS_ROOT}/Headers/Public/SlackTextViewController/Classes"
+OTHER_LDFLAGS = -ObjC
+PODS_ROOT = ${SRCROOT}
+SKIP_INSTALL = YES
\ No newline at end of file
diff --git a/src/ui/ios/GF-Offline-Translator/Pods/Target Support Files/Pods-SlackTextViewController/Pods-SlackTextViewController-dummy.m b/src/ui/ios/GF-Offline-Translator/Pods/Target Support Files/Pods-SlackTextViewController/Pods-SlackTextViewController-dummy.m
new file mode 100644
index 000000000..f66e118b8
--- /dev/null
+++ b/src/ui/ios/GF-Offline-Translator/Pods/Target Support Files/Pods-SlackTextViewController/Pods-SlackTextViewController-dummy.m
@@ -0,0 +1,5 @@
+#import
+@interface PodsDummy_Pods_SlackTextViewController : NSObject
+@end
+@implementation PodsDummy_Pods_SlackTextViewController
+@end
diff --git a/src/ui/ios/GF-Offline-Translator/Pods/Target Support Files/Pods-SlackTextViewController/Pods-SlackTextViewController-prefix.pch b/src/ui/ios/GF-Offline-Translator/Pods/Target Support Files/Pods-SlackTextViewController/Pods-SlackTextViewController-prefix.pch
new file mode 100644
index 000000000..95cf11d9f
--- /dev/null
+++ b/src/ui/ios/GF-Offline-Translator/Pods/Target Support Files/Pods-SlackTextViewController/Pods-SlackTextViewController-prefix.pch
@@ -0,0 +1,5 @@
+#ifdef __OBJC__
+#import
+#endif
+
+#import "Pods-environment.h"
diff --git a/src/ui/ios/GF-Offline-Translator/Pods/Target Support Files/Pods-SlackTextViewController/Pods-SlackTextViewController.xcconfig b/src/ui/ios/GF-Offline-Translator/Pods/Target Support Files/Pods-SlackTextViewController/Pods-SlackTextViewController.xcconfig
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/ui/ios/GF-Offline-Translator/Pods/Target Support Files/Pods/Pods-acknowledgements.markdown b/src/ui/ios/GF-Offline-Translator/Pods/Target Support Files/Pods/Pods-acknowledgements.markdown
new file mode 100644
index 000000000..f6b96022c
--- /dev/null
+++ b/src/ui/ios/GF-Offline-Translator/Pods/Target Support Files/Pods/Pods-acknowledgements.markdown
@@ -0,0 +1,21 @@
+# Acknowledgements
+This application makes use of the following third party libraries:
+
+## SlackTextViewController
+
+
+ Copyright 2015 Slack Technologies, Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+Generated by CocoaPods - http://cocoapods.org
diff --git a/src/ui/ios/GF-Offline-Translator/Pods/Target Support Files/Pods/Pods-acknowledgements.plist b/src/ui/ios/GF-Offline-Translator/Pods/Target Support Files/Pods/Pods-acknowledgements.plist
new file mode 100644
index 000000000..bb5d70f94
--- /dev/null
+++ b/src/ui/ios/GF-Offline-Translator/Pods/Target Support Files/Pods/Pods-acknowledgements.plist
@@ -0,0 +1,51 @@
+
+
+
+
+ PreferenceSpecifiers
+
+
+ FooterText
+ This application makes use of the following third party libraries:
+ Title
+ Acknowledgements
+ Type
+ PSGroupSpecifier
+
+
+ FooterText
+
+ Copyright 2015 Slack Technologies, Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ Title
+ SlackTextViewController
+ Type
+ PSGroupSpecifier
+
+
+ FooterText
+ Generated by CocoaPods - http://cocoapods.org
+ Title
+
+ Type
+ PSGroupSpecifier
+
+
+ StringsTable
+ Acknowledgements
+ Title
+ Acknowledgements
+
+
diff --git a/src/ui/ios/GF-Offline-Translator/Pods/Target Support Files/Pods/Pods-dummy.m b/src/ui/ios/GF-Offline-Translator/Pods/Target Support Files/Pods/Pods-dummy.m
new file mode 100644
index 000000000..ade64bd1a
--- /dev/null
+++ b/src/ui/ios/GF-Offline-Translator/Pods/Target Support Files/Pods/Pods-dummy.m
@@ -0,0 +1,5 @@
+#import
+@interface PodsDummy_Pods : NSObject
+@end
+@implementation PodsDummy_Pods
+@end
diff --git a/src/ui/ios/GF-Offline-Translator/Pods/Target Support Files/Pods/Pods-environment.h b/src/ui/ios/GF-Offline-Translator/Pods/Target Support Files/Pods/Pods-environment.h
new file mode 100644
index 000000000..c4b8767fa
--- /dev/null
+++ b/src/ui/ios/GF-Offline-Translator/Pods/Target Support Files/Pods/Pods-environment.h
@@ -0,0 +1,14 @@
+
+// To check if a library is compiled with CocoaPods you
+// can use the `COCOAPODS` macro definition which is
+// defined in the xcconfigs so it is available in
+// headers also when they are imported in the client
+// project.
+
+
+// SlackTextViewController
+#define COCOAPODS_POD_AVAILABLE_SlackTextViewController
+#define COCOAPODS_VERSION_MAJOR_SlackTextViewController 1
+#define COCOAPODS_VERSION_MINOR_SlackTextViewController 5
+#define COCOAPODS_VERSION_PATCH_SlackTextViewController 2
+
diff --git a/src/ui/ios/GF-Offline-Translator/Pods/Target Support Files/Pods/Pods-resources.sh b/src/ui/ios/GF-Offline-Translator/Pods/Target Support Files/Pods/Pods-resources.sh
new file mode 100644
index 000000000..0adecdd33
--- /dev/null
+++ b/src/ui/ios/GF-Offline-Translator/Pods/Target Support Files/Pods/Pods-resources.sh
@@ -0,0 +1,78 @@
+#!/bin/sh
+set -e
+
+mkdir -p "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
+
+RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt
+> "$RESOURCES_TO_COPY"
+
+XCASSET_FILES=""
+
+install_resource()
+{
+ case $1 in
+ *.storyboard)
+ echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc ${PODS_ROOT}/$1 --sdk ${SDKROOT}"
+ ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc" "${PODS_ROOT}/$1" --sdk "${SDKROOT}"
+ ;;
+ *.xib)
+ echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib ${PODS_ROOT}/$1 --sdk ${SDKROOT}"
+ ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib" "${PODS_ROOT}/$1" --sdk "${SDKROOT}"
+ ;;
+ *.framework)
+ echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
+ mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
+ echo "rsync -av ${PODS_ROOT}/$1 ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
+ rsync -av "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
+ ;;
+ *.xcdatamodel)
+ echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1"`.mom\""
+ xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodel`.mom"
+ ;;
+ *.xcdatamodeld)
+ echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd\""
+ xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd"
+ ;;
+ *.xcmappingmodel)
+ echo "xcrun mapc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcmappingmodel`.cdm\""
+ xcrun mapc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcmappingmodel`.cdm"
+ ;;
+ *.xcassets)
+ XCASSET_FILES="$XCASSET_FILES '${PODS_ROOT}/$1'"
+ ;;
+ /*)
+ echo "$1"
+ echo "$1" >> "$RESOURCES_TO_COPY"
+ ;;
+ *)
+ echo "${PODS_ROOT}/$1"
+ echo "${PODS_ROOT}/$1" >> "$RESOURCES_TO_COPY"
+ ;;
+ esac
+}
+
+rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
+if [[ "${ACTION}" == "install" ]]; then
+ rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
+fi
+rm -f "$RESOURCES_TO_COPY"
+
+if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ]
+then
+ case "${TARGETED_DEVICE_FAMILY}" in
+ 1,2)
+ TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone"
+ ;;
+ 1)
+ TARGET_DEVICE_ARGS="--target-device iphone"
+ ;;
+ 2)
+ TARGET_DEVICE_ARGS="--target-device ipad"
+ ;;
+ *)
+ TARGET_DEVICE_ARGS="--target-device mac"
+ ;;
+ esac
+ while read line; do XCASSET_FILES="$XCASSET_FILES '$line'"; done <<<$(find "$PWD" -name "*.xcassets" | egrep -v "^$PODS_ROOT")
+ echo $XCASSET_FILES | xargs actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${IPHONEOS_DEPLOYMENT_TARGET}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
+fi
diff --git a/src/ui/ios/GF-Offline-Translator/Pods/Target Support Files/Pods/Pods.debug.xcconfig b/src/ui/ios/GF-Offline-Translator/Pods/Target Support Files/Pods/Pods.debug.xcconfig
new file mode 100644
index 000000000..272611575
--- /dev/null
+++ b/src/ui/ios/GF-Offline-Translator/Pods/Target Support Files/Pods/Pods.debug.xcconfig
@@ -0,0 +1,6 @@
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/SlackTextViewController" "${PODS_ROOT}/Headers/Public/SlackTextViewController/Additions" "${PODS_ROOT}/Headers/Public/SlackTextViewController/Classes"
+OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/SlackTextViewController" -isystem "${PODS_ROOT}/Headers/Public/SlackTextViewController/Additions" -isystem "${PODS_ROOT}/Headers/Public/SlackTextViewController/Classes"
+OTHER_LDFLAGS = $(inherited) -ObjC -l"Pods-SlackTextViewController"
+OTHER_LIBTOOLFLAGS = $(OTHER_LDFLAGS)
+PODS_ROOT = ${SRCROOT}/Pods
\ No newline at end of file
diff --git a/src/ui/ios/GF-Offline-Translator/Pods/Target Support Files/Pods/Pods.release.xcconfig b/src/ui/ios/GF-Offline-Translator/Pods/Target Support Files/Pods/Pods.release.xcconfig
new file mode 100644
index 000000000..272611575
--- /dev/null
+++ b/src/ui/ios/GF-Offline-Translator/Pods/Target Support Files/Pods/Pods.release.xcconfig
@@ -0,0 +1,6 @@
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/SlackTextViewController" "${PODS_ROOT}/Headers/Public/SlackTextViewController/Additions" "${PODS_ROOT}/Headers/Public/SlackTextViewController/Classes"
+OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/SlackTextViewController" -isystem "${PODS_ROOT}/Headers/Public/SlackTextViewController/Additions" -isystem "${PODS_ROOT}/Headers/Public/SlackTextViewController/Classes"
+OTHER_LDFLAGS = $(inherited) -ObjC -l"Pods-SlackTextViewController"
+OTHER_LIBTOOLFLAGS = $(OTHER_LDFLAGS)
+PODS_ROOT = ${SRCROOT}/Pods
\ No newline at end of file